提交 dc874fdc 编写于 作者: chenxiang@lab.ibiz5.com's avatar chenxiang@lab.ibiz5.com

1

上级 cbe60b63
*2020-3-7*
初始化
MIT License
Copyright (c) 2020 千反田丷
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# VUE_R7
#### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
\ No newline at end of file
## 前言
# app
### 1. 简介
该web应用是基于iBiz平台提供的一套PC端前端解决方案Vue_R7【**一套基于Vue全家桶(Vue + Vue-router + Vuex)的前端框架**】生产而成,Vue_R7不仅适用于管理后台或管理系统开发,且广泛适用于B/S架构的项目开发。本文档主要介绍项目如何快速上手,成果物代码结构做一阐述,旨在能够为开发人员提供一定开发指导支持。而今框架开源,希望能有更多志同道合的伙伴参与Vue_R7的迭代 ^_^
### 2. 开发环境要求
- Node.js
- Yarn
- Vue Cli
### 3. 开发技术要求
掌握`Vue``TypeScript``less``html`等技术。
### 4. 技术栈
- 前端MVVM框架:vue.js 2.6.10
- 路由:vue-router 3.1.3
- 状态管理:vue-router 3.1.3
- 国际化:vue-i18n 8.15.3
- 数据交互:axios 0.19.1
- UI框架:element-ui 2.13.0, view-design 4.1.0
- 工具库:qs, path-to-regexp, rxjs
- 图标库:font-awesome 4.7.0
- 引入组件: tinymce 4.8.5
- 代码风格检测:eslint
## 快速上手
### 1. 开发环境
> 在安装使用 `Yarn` 和 `Vue Cli (3.0)` 前,务必确认 [Node.js](https://nodejs.org) 已经升级到 v4.8.0 或以上,强烈建议升级至最新版本。
> 如果你想了解更多 `Yarn` 工具链的功能和命令,建议访问 [Yarn](https://yarnpkg.com) 了解更多。
> 如果你想了解更多 `Vue Cli (3.0)` 工具链的功能和命令,建议访问 [Vue Cli (3.0)](https://cli.vuejs.org/) 了解更多。
- 访问 [Node.js](https://nodejs.org) ,根据文档安装 `Node.js`
- 访问 [Yarn](https://yarnpkg.com) ,根据文档安装 `Yarn`
- 访问 [Vue Cli (3.0)](https://cli.vuejs.org/) ,根据文档安装 `Vue Cli (3.0)`
<blockquote style="border-color: red;"><p>在安装 Vue Cli (3.0) ,请使用 Yarn 模式全局安装。</p></blockquote>
```bash
$ yarn global add @vue/cli
## Project setup
```
以下为 Windows 环境开发正常配置
<br>
<br>
![开发环境信息](./imgs/getting-started/development.png)
### 2. 安装依赖
打开前端项目,进入工作空间下,执行安装依赖命令
```bash
$ yarn install
yarn install
```
### 3. 启动
在工作空间下,执行启动命令
```bash
$ yarn dev-serve
### Compiles and hot-reloads for development
```
启动后,通过 vue.config.js 开发服务 devServer 下配置的本地启动端口号访问开发项目。<br>
示例:
```bash
$ http://localhost:8111
yarn serve
```
这儿需要注意一点,此时启动的项目访问的数据是我们前端的mock数据,如需与后台直接交互,请看第4点。
### 4. 远程代理
在工作空间下,执行启动命令
```bash
$ yarn serve
### Compiles and minifies for production
```
修改远程代理文件 vue.config.js 代理地址
![远程代理地址](./imgs/getting-started/proxy.png)
### 5. 打包
在工作空间下,执行打包命令
```bash
$ yarn build
yarn build
```
打包完成,生成最终交付产物。
## 成果物结构
### Run your unit tests
```
|─ ─ app
​ |─ ─ public public文件夹
​ |─ ─ assets 静态文件夹
|─ ─ favicon.ico 图标
​ |─ ─ src 工程文件夹
|─ ─ assets 静态资源
|─ ─ components 基础组件,主要包含编辑器组件和其他全局使用的组件
|─ ─ engine 引擎文件,主要封装了内置视图的内置逻辑
|─ ─ environments 环境文件
​ |─ ─ interface 接口文件
​ |─ ─ locale 多语言文件
|─ ─ mock 模拟数据
|─ ─ pages 视图文件夹
|─ ─ module 模块名称
​ |─ ─ XXX-view 视图文件夹
|─ ─ XXX-view-base.vue 视图基类
|─ ─ XXX-view.vue 自定义视图文件
|─ ─ XXX-view.less 自定义视图样式文件
​ |─ ─ main.ts 应用主函数入口
​ |─ ─ page-register.ts 全局视图注册
​ |─ ─ router.ts 路由配置文件
|─ ─ service 应用实体数据服务文件夹
|─ ─ XXX 应用实体名称
|─ ─ XXX-service-base.ts 应用实体数据服务文件
|─ ─ XXX-service.ts 自定义应用实体数据服务文件
|─ ─ YYY-logic-base.ts 应用实体数据处理逻辑文件
|─ ─ YYY-logic.ts 自定义应用实体数据处理逻辑文件
|─ ─ store 全局状态管理
|─ ─ styles 样式文件夹
|─ ─ default.less 默认样式
|─ ─ user.less 用户自定义样式
|─ ─ theme 主题文件夹
|─ ─ uiservice 界面服务文件
|─ ─ XXX 应用实体名称
|─ ─ XXX-ui-service-base.ts 应用实体界面服务文件
|─ ─ XXX-ui-service.ts 自定义应用实体界面服务文件
|─ ─ YYY-ui-logic-base.ts 应用实体界面处理逻辑文件
|─ ─ YYY-ui-logic.ts 自定义应用实体界面处理逻辑文件
|─ ─ utils 工具类文件
|─ ─ widgets 部件文件夹
|─ ─ appde 应用实体名称
​ |─ ─ XXX 部件名称
|─ ─ XXX-base.vue 视图基类
|─ ─ XXX.vue 自定义部件文件
|─ ─ XXX.less 部件样式文件
​ |─ ─ XXX.model.ts 部件model文件
​ |─ ─ XXX.service.ts 部件服务文件
|─ ─ app-register.ts 公共组件全局注册
​ |─ ─ App.vue 入口组件
​ |─ ─ user-register.ts 自定义组件全局注册
​|─ ─ package.json 依赖管理文件
​ |─ ─ vue.config.js vue cli 配置
yarn test:unit
```
## 更新日志
每个版本的详细更改都记录在[发行说明](CHANGELOG.md)中。
## 如何贡献
如果你希望参与贡献,欢迎 [Pull Request](<http://demo.ibizlab.cn/ibiz_r7/vue_r7/issues/new>),或通过自助服务群给我们报告 Bug。
强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)(本指南不提供此项目的实际支持服务!)[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545)[《如何有效地报告 Bug》](https://www.chiark.greenend.org.uk/~sgtatham/bugs-cn.html)[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。
## 社区互助
1.[iBizLab论坛](https://bbs.ibizlab.cn/)
### Run your end-to-end tests
```
yarn test:e2e
```
2.加入钉钉 Vue_R7自助服务群(中文)
### Lints and fixes files
```
yarn lint
```
<img src="./imgs/getting-started/vue-r7-group.png" height="400" width="400">
\ No newline at end of file
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
{
"name": "app",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"adler-32": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz",
"integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=",
"requires": {
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
}
},
"cfb": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.1.4.tgz",
"integrity": "sha512-rwFkl3aFO3f+ljR27YINwC0x8vPjyiEVbYbrTCKzspEf7Q++3THdfHVgJYNUbxNcupJECrLX+L40Mjm9hm/Bgw==",
"requires": {
"adler-32": "~1.2.0",
"commander": "^2.16.0",
"crc-32": "~1.2.0",
"printj": "~1.1.2"
}
},
"codepage": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.14.0.tgz",
"integrity": "sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k=",
"requires": {
"commander": "~2.14.1",
"exit-on-epipe": "~1.0.1"
},
"dependencies": {
"commander": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
}
}
},
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
},
"crc-32": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
"requires": {
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
}
},
"exit-on-epipe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
},
"file-saver": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
"integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw=="
},
"frac": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
},
"printj": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
},
"raw-loader": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
"integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
"dev": true
},
"script-loader": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/script-loader/-/script-loader-0.7.2.tgz",
"integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
"dev": true,
"requires": {
"raw-loader": "~0.5.1"
}
},
"ssf": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.10.3.tgz",
"integrity": "sha512-pRuUdW0WwyB2doSqqjWyzwCD6PkfxpHAHdZp39K3dp/Hq7f+xfMwNAWIi16DyrRg4gg9c/RvLYkJTSawTPTm1w==",
"requires": {
"frac": "~1.1.2"
}
},
"wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="
},
"xlsx": {
"version": "0.15.6",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.15.6.tgz",
"integrity": "sha512-7vD9eutyLs65iDjNFimVN+gk/oDkfkCgpQUjdE82QgzJCrBHC4bGPH7fzKVyy0UPp3gyFVQTQEFJaWaAvZCShQ==",
"requires": {
"adler-32": "~1.2.0",
"cfb": "^1.1.4",
"codepage": "~1.14.0",
"commander": "~2.17.1",
"crc-32": "~1.2.0",
"exit-on-epipe": "~1.0.1",
"ssf": "~0.10.3",
"wmf": "~1.0.1"
}
}
}
}
public/assets/img/logo.png

6.7 KB | W: | H:

public/assets/img/logo.png

2.0 KB | W: | H:

public/assets/img/logo.png
public/assets/img/logo.png
public/assets/img/logo.png
public/assets/img/logo.png
  • 2-up
  • Swipe
  • Onion skin
tinymce.addI18n('en_US',{
"Cut": "Cut",
"Heading 5": "Heading 5",
"Header 2": "Header 2",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.",
"Heading 4": "Heading 4",
"Div": "Div",
"Heading 2": "Heading 2",
"Paste": "Paste",
"Close": "Close",
"Font Family": "Font Family",
"Pre": "Pre",
"Align right": "Align right",
"New document": "New document",
"Blockquote": "Blockquote",
"Numbered list": "Numbered list",
"Heading 1": "Heading 1",
"Headings": "Headings",
"Increase indent": "Increase indent",
"Formats": "Formats",
"Headers": "Headers",
"Select all": "Select all",
"Header 3": "Header 3",
"Blocks": "Blocks",
"Undo": "Undo",
"Strikethrough": "Strike-through",
"Bullet list": "Bullet list",
"Header 1": "Header 1",
"Superscript": "Superscript",
"Clear formatting": "Clear formatting",
"Font Sizes": "Font Sizes",
"Subscript": "Subscript",
"Header 6": "Header 6",
"Redo": "Redo",
"Paragraph": "Paragraph",
"Ok": "Ok",
"Bold": "Bold",
"Code": "Code",
"Italic": "Italic",
"Align center": "Align centre",
"Header 5": "Header 5",
"Heading 6": "Heading 6",
"Heading 3": "Heading 3",
"Decrease indent": "Decrease indent",
"Header 4": "Header 4",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.",
"Underline": "Underline",
"Cancel": "Cancel",
"Justify": "Justify",
"Inline": "Inline",
"Copy": "Copy",
"Align left": "Align left",
"Visual aids": "Visual aids",
"Lower Greek": "Lower Greek",
"Square": "Square",
"Default": "Default",
"Lower Alpha": "Lower Alpha",
"Circle": "Circle",
"Disc": "Disc",
"Upper Alpha": "Upper Alpha",
"Upper Roman": "Upper Roman",
"Lower Roman": "Lower Roman",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.",
"Name": "Name",
"Anchor": "Anchor",
"Id": "ID",
"You have unsaved changes are you sure you want to navigate away?": "You have unsaved changes are you sure you want to navigate away?",
"Restore last draft": "Restore last draft",
"Special character": "Special character",
"Source code": "Source code",
"Language": "Language",
"Insert\/Edit code sample": "Insert\/Edit code sample",
"B": "B",
"R": "R",
"G": "G",
"Color": "Colour",
"Right to left": "Right to left",
"Left to right": "Left to right",
"Emoticons": "Emoticons",
"Robots": "Robots",
"Document properties": "Document properties",
"Title": "Title",
"Keywords": "Keywords",
"Encoding": "Encoding",
"Description": "Description",
"Author": "Author",
"Fullscreen": "Full-screen",
"Horizontal line": "Horizontal line",
"Horizontal space": "Horizontal space",
"Insert\/edit image": "Insert\/edit image",
"General": "General",
"Advanced": "Advanced",
"Source": "Source",
"Border": "Border",
"Constrain proportions": "Constrain proportions",
"Vertical space": "Vertical space",
"Image description": "Image description",
"Style": "Style",
"Dimensions": "Dimensions",
"Insert image": "Insert image",
"Image": "Image",
"Zoom in": "Zoom in",
"Contrast": "Contrast",
"Back": "Back",
"Gamma": "Gamma",
"Flip horizontally": "Flip horizontally",
"Resize": "Resize",
"Sharpen": "Sharpen",
"Zoom out": "Zoom out",
"Image options": "Image options",
"Apply": "Apply",
"Brightness": "Brightness",
"Rotate clockwise": "Rotate clockwise",
"Rotate counterclockwise": "Rotate counterclockwise",
"Edit image": "Edit image",
"Color levels": "Colour levels",
"Crop": "Crop",
"Orientation": "Orientation",
"Flip vertically": "Flip vertically",
"Invert": "Invert",
"Date\/time": "Date\/time",
"Insert date\/time": "Insert date\/time",
"Remove link": "Remove link",
"Url": "URL",
"Text to display": "Text to display",
"Anchors": "Anchors",
"Insert link": "Insert link",
"Link": "Link",
"New window": "New window",
"None": "None",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?",
"Paste or type a link": "Paste or type a link",
"Target": "Target",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",
"Insert\/edit link": "Insert\/edit link",
"Insert\/edit video": "Insert\/edit video",
"Media": "Media",
"Alternative source": "Alternative source",
"Paste your embed code below:": "Paste your embed code below:",
"Insert video": "Insert video",
"Poster": "Poster",
"Insert\/edit media": "Insert\/edit media",
"Embed": "Embed",
"Nonbreaking space": "Non-breaking space",
"Page break": "Page break",
"Paste as text": "Paste as text",
"Preview": "Preview",
"Print": "Print",
"Save": "Save",
"Could not find the specified string.": "Could not find the specified string.",
"Replace": "Replace",
"Next": "Next",
"Whole words": "Whole words",
"Find and replace": "Find and replace",
"Replace with": "Replace with",
"Find": "Find",
"Replace all": "Replace all",
"Match case": "Match case",
"Prev": "Prev",
"Spellcheck": "Spell-check",
"Finish": "Finish",
"Ignore all": "Ignore all",
"Ignore": "Ignore",
"Add to Dictionary": "Add to Dictionary",
"Insert row before": "Insert row before",
"Rows": "Rows",
"Height": "Height",
"Paste row after": "Paste row after",
"Alignment": "Alignment",
"Border color": "Border colour",
"Column group": "Column group",
"Row": "Row",
"Insert column before": "Insert column before",
"Split cell": "Split cell",
"Cell padding": "Cell padding",
"Cell spacing": "Cell spacing",
"Row type": "Row type",
"Insert table": "Insert table",
"Body": "Body",
"Caption": "Caption",
"Footer": "Footer",
"Delete row": "Delete row",
"Paste row before": "Paste row before",
"Scope": "Scope",
"Delete table": "Delete table",
"H Align": "H Align",
"Top": "Top",
"Header cell": "Header cell",
"Column": "Column",
"Row group": "Row group",
"Cell": "Cell",
"Middle": "Middle",
"Cell type": "Cell type",
"Copy row": "Copy row",
"Row properties": "Row properties",
"Table properties": "Table properties",
"Bottom": "Bottom",
"V Align": "V Align",
"Header": "Header",
"Right": "Right",
"Insert column after": "Insert column after",
"Cols": "Cols",
"Insert row after": "Insert row after",
"Width": "Width",
"Cell properties": "Cell properties",
"Left": "Left",
"Cut row": "Cut row",
"Delete column": "Delete column",
"Center": "Centre",
"Merge cells": "Merge cells",
"Insert template": "Insert template",
"Templates": "Templates",
"Background color": "Background colour",
"Custom...": "Custom...",
"Custom color": "Custom colour",
"No color": "No colour",
"Text color": "Text colour",
"Table of Contents": "Table of Contents",
"Show blocks": "Show blocks",
"Show invisible characters": "Show invisible characters",
"Words: {0}": "Words: {0}",
"Insert": "Insert",
"File": "File",
"Edit": "Edit",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help",
"Tools": "Tools",
"View": "View",
"Table": "Table",
"Format": "Format"
});
\ No newline at end of file
window.Environment = {
// 是否为开发模式
devMode: true,
// 配置平台地址
StudioUrl: "http://neko.org.cn:3335/",
// 中心标识
SlnId: "AFFB70EF-A2CF-470A-B1AE-38156EAF4A50",
// 系统标识
SysId: "8A26FD38-06D7-495C-88B1-77238F3AB96D",
// 前端应用标识
AppId: "0df1f39e51db7152428a905a11c28a16"
}
\ No newline at end of file
public/favicon.ico

4.2 KB | W: | H:

public/favicon.ico

1.1 KB | W: | H:

public/favicon.ico
public/favicon.ico
public/favicon.ico
public/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
<template>
<div id='app'>
<debug-actions/>
<router-view v-if="isRouterAlive"/>
</div>
</template>
......
......@@ -60,6 +60,14 @@ import AppActionBar from './components/app-actionbar/app-actionbar.vue'
import AppOrgSector from './components/app-orgsector/app-orgsector.vue'
import AppBuild from './components/app-build/app-build.vue'
import AppStudioAction from './components/app-studioaction/app-studioaction.vue'
import AppDebugActions from './components/app-debug-actions/app-debug-actions.vue'
import AppHeaderMenus from './components/app-header-menus/app-header-menus.vue'
import AppColumnLink from './components/app-column-link/app-column-link.vue'
import AppDataUploadView from './components/app-data-upload/app-data-upload.vue'
import DropDownListDynamic from './components/dropdown-list-dynamic/dropdown-list-dynamic.vue'
import AppImagePreview from './components/app-image-preview/app-image-preview.vue'
import AppFormatData from './components/app-format-data/app-format-data.vue'
import AppUploadFileInfo from './components/app-upload-file-info/app-upload-file-info.vue'
// 全局挂载UI实体服务注册中心
window['uiServiceRegister'] = uiServiceRegister;
......@@ -129,5 +137,13 @@ export const AppComponents = {
v.component('app-orgsector', AppOrgSector);
v.component('app-build', AppBuild);
v.component('app-studioaction', AppStudioAction);
v.component('app-debug-actions', AppDebugActions);
v.component('app-header-menus', AppHeaderMenus);
v.component('app-column-link', AppColumnLink);
v.component('app-data-upload', AppDataUploadView);
v.component('dropdown-list-dynamic', DropDownListDynamic);
v.component('app-image-preview', AppImagePreview);
v.component('app-format-data', AppFormatData);
v.component('app-upload-file-info', AppUploadFileInfo);
},
};
\ No newline at end of file
......@@ -5,7 +5,6 @@ html, body {
.app-error-view {
height: 100%;
width: 100%;
position: relative;
.app-error-container {
height: 380px;
width: 670px;
......
......@@ -3,8 +3,8 @@
<div class="app-error-container">
<img src="/assets/img/404.png" />
<div class="error-text">
<div class="error-text1">抱歉,您访问的页面不存在!</div>
<div class="error-text2">您要找的页面存在,请返回 <a on-click={this.gotoIndexView}>首页</a> 继续浏览</div>
<div class="error-text1">{{$t('components.404.errorText1')}}</div>
<div class="error-text2">{{$t('components.404.errorText2')}} <a @click="gotoIndexView">{{$t('components.404.indexPage')}}</a> {{$t('components.404.continue')}}</div>
</div>
</div>
</div>
......@@ -13,7 +13,8 @@
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
@Component({})
@Component({
})
export default class Error404 extends Vue {
/**
......
html, body {
height: 100%;
}
.app-error-view {
height: 100%;
width: 100%;
position: relative;
.app-error-container {
height: 380px;
width: 670px;
......
......@@ -3,8 +3,8 @@
<div class="app-error-container">
<img src="/assets/img/500.png" />
<div class="error-text">
<div class="error-text1">抱歉,服务器出错了!</div>
<div class="error-text2">服务器出错了,请返回 <a on-click={this.gotoIndexView}>首页</a> 继续浏览</div>
<div class="error-text1">{{$t('components.500.errorText1')}}</div>
<div class="error-text2">{{$t('components.500.errorText2')}} <a @click="gotoIndexView">{{$t('components.500.indexPage')}}</a> {{$t('components.500.continue')}}</div>
</div>
</div>
</div>
......@@ -13,7 +13,8 @@
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
@Component({})
@Component({
})
export default class Error404 extends Vue {
/**
......
......@@ -23,7 +23,8 @@ import { Component, Vue, Prop, Model, Watch } from 'vue-property-decorator';
import { Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
@Component({})
@Component({
})
export default class AppAutocomplete extends Vue {
/**
......@@ -195,16 +196,21 @@ export default class AppAutocomplete extends Vue {
let _parentdata = this.$util.formatData(this.data,this.itemParam.parentdata);
Object.assign(param,_parentdata);
}
// 错误信息国际化
let error: string = (this.$t('components.appAutocomplete.error') as any);
let miss: string = (this.$t('components.appAutocomplete.miss') as any);
let requestException: string = (this.$t('components.appAutocomplete.requestException') as any);
if(!this.service){
this.$Notice.error({ title: '错误', desc: '缺少参数service' });
this.$Notice.error({ title: error, desc: miss+'service' });
} else if(!this.acParams.serviceName) {
this.$Notice.error({ title: '错误', desc: '缺少参数serviceName' });
this.$Notice.error({ title: error, desc: miss+'serviceName' });
} else if(!this.acParams.interfaceName) {
this.$Notice.error({ title: '错误', desc: '缺少参数interfaceName' });
this.$Notice.error({ title: error, desc: miss+'interfaceName' });
} else {
this.service.getItems(this.acParams.serviceName,this.acParams.interfaceName, context, param).then((response: any) => {
if (!response) {
this.$Notice.error({ title: '错误', desc: '请求异常' });
this.$Notice.error({ title: error, desc: requestException });
} else {
this.items = [...response];
}
......
......@@ -6,7 +6,7 @@
</i-button>
</button-group>
<button-group v-show="showTypeDir">
<el-tooltip content="定制">
<el-tooltip :content="$t('components.appBuild.custom')">
<i-button icon="md-build" type="primary" @click="handleClick"></i-button>
</el-tooltip>
<i-button class="collapse-btn" type="primary" @click="clickCollapse('right')">
......@@ -21,8 +21,6 @@ import {Vue, Component, Prop, Watch} from 'vue-property-decorator';
@Component({
components: {
}
})
export default class AppBuild extends Vue {
......
<template>
<FullCalendar ref="appCalendar" height="parent" :locales="locales" locale="zh-cn" :header="header" class="app-calendar" :events="items" defaultView="dayGridMonth" :plugins="calendarPlugins" @eventClick="onClick"/>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
import FullCalendar from '@fullcalendar/vue';
import dayGridPlugin from '@fullcalendar/daygrid';
import allLocales from '@fullcalendar/core/locales-all';
import '@fullcalendar/core/main.css';
import '@fullcalendar/daygrid/main.css';
@Component({
components: {
FullCalendar
}
})
export default class AppCalendar extends Vue {
/**
* 数据集
*
* @type {string}
* @memberof AppCalendar
*/
@Prop() datas?: string;
/**
* 类型
*
* @type {string}
* @memberof aaa
*/
@Prop() type?: string;
/**
* 数据集对象
*
* @protected
* @type {any[]}
* @memberof AppCalendar
*/
protected items: any[] = [];
/**
* 头部设置
*
* @protected
* @memberof AppCalendar
*/
protected header: any = {
left: 'prev,next today',
center: 'title',
right: false
};
/**
* 语言资源
*
* @protected
* @memberof AppCalendar
*/
public locales = allLocales;
/**
* 日历插件
*
* @protected
* @type {any[]}
* @memberof aaa
*/
protected calendarPlugins: any[] = [dayGridPlugin];
/**
* 数据集监听
*
* @param {*} newVal
* @param {*} oldVal
* @memberof AppCalendar
*/
@Watch('datas')
onDatasChange(newVal: any, oldVal: any) {
if(newVal) {
this.items = this.prepareItems(JSON.parse(newVal));
}
}
/**
* 类型监听
*
* @param {*} newVal
* @param {*} oldVal
* @memberof AppCalendar
*/
@Watch('type')
onTypeChange(newVal: any, oldVal: any) {
if(Object.is(newVal, 'calendar')) {
let appCalendar: any = this.$refs.appCalendar;
let api = appCalendar.getApi();
api.updateSize()
}
}
/**
* 生命周期
*
* @memberof AppCalendar
*/
public mounted() {
if(this.datas) {
this.items = this.prepareItems(JSON.parse(this.datas));
}
}
/**
* 准备数据
*
* @param {*} datas
* @memberof AppCalendar
*/
public prepareItems(datas: any[]): any[] {
let items: any[] = [];
datas.forEach((data: any) => {
if(!data.hasOwnProperty('date')) {
data.date = data.create_date;
data.id = data.srfkey;
}
items.push(data);
})
return items;
}
/**
* 数据点击
*
* @param {*} datas
* @memberof AppCalendar
*/
public onClick($event: any) {
let item = this.items.find((item: any) => item.srfkey == $event.event.id);
if(item) {
this.$emit('click', item);
}
}
}
</script>
<style lang="less">
.app-calendar {
height: 100%;
width: 100%;
}
</style>
\ No newline at end of file
<template>
<checkbox-group class="app-checkbox-list" v-model="selectArray">
<checkbox v-for="(item,index) in items" :key="index" :label="item.value" :disabled="isDisabled || item.disabled">
<span>{{item.text}}</span>
<span>{{Object.is(codelistType,'STATIC') ? $t('codelist.'+tag+'.'+item.value) : item.text}}</span>
</checkbox>
</checkbox-group >
</template>
......@@ -10,7 +10,8 @@
import { Component, Vue, Prop, Model } from 'vue-property-decorator';
import CodeListService from "@service/app/codelist-service";
@Component({})
@Component({
})
export default class AppCheckBox extends Vue {
/**
* 代码表服务对象
......@@ -36,6 +37,14 @@ export default class AppCheckBox extends Vue {
*/
@Prop() public codelistType?: string;
/**
* 代码表值分隔符
*
* @type {string}
* @memberof AppCheckBox
*/
@Prop({default:';'}) public valueSeparator?: string;
/**
* 是否禁用
*
......@@ -136,7 +145,7 @@ export default class AppCheckBox extends Vue {
return selectsArray;
} else if (Object.is(this.currentmode, 'str')) {
if (this.selects !== '') {
return this.selects.split(this.currentseparator);
return this.selects.split(this.valueSeparator);
}
}
} else {
......@@ -166,7 +175,7 @@ export default class AppCheckBox extends Vue {
}
_datas.push(item.value);
});
value = _datas.join(this.currentseparator);
value = _datas.join(this.valueSeparator);
}
this.$emit('change', value);
}
......@@ -191,13 +200,13 @@ export default class AppCheckBox extends Vue {
if (codelist) {
this.items = [...JSON.parse(JSON.stringify(codelist.items))];
} else {
console.log(`----${this.tag}----代码表不存在`);
console.log(`----${this.tag}----$t('components.appCheckBox.notExist')`);
}
} else if (Object.is(this.codelistType,"DYNAMIC")) {
this.codeListService.getItems(this.tag).then((res:any) => {
this.items = res;
}).catch((error:any) => {
console.log(`----${this.tag}----代码表不存在`);
console.log(`----${this.tag}----$t('components.appCheckBox.notExist')`);
});
}
}
......
.app-column-link{
display: inline-block;
cursor:pointer;
}
\ No newline at end of file
<template>
<a class="app-column-link" @click="openLinkView($event)">
<slot></slot>
</a>
</template>
<script lang="ts">
import { Vue, Component,Prop } from 'vue-property-decorator';
import { Subject } from 'rxjs';
/**
* 表格列链接
*/
@Component({
})
export default class AppColumnLink extends Vue {
/**
* 表格行数据
*
* @type {*}
* @memberof AppColumnLink
*/
@Prop() public data!: any;
/**
* 数据链接视图
*
* @type {*}
* @memberof AppColumnLink
*/
@Prop() public linkview?: any;
/**
* 值项名称
*
* @type {string}
* @memberof AppColumnLink
*/
@Prop() public valueitem?: string;
/**
* 导航上下文
*
* @type {*}
* @memberof AppColumnLink
*/
@Prop({default:{}}) public context?:any;
/**
* 导航参数
*
* @type {*}
* @memberof AppColumnLink
*/
@Prop({default:{}}) public viewparams?:any;
/**
* 应用实体主键属性名称
*
* @type {string}
* @memberof AppColumnLink
*/
@Prop() public deKeyField!:string;
/**
* 打开链接视图
*
* @memberof AppColumnLink
*/
public openLinkView($event: any): void {
$event.stopPropagation();
if (!this.data || !this.valueitem || !this.data[this.valueitem]) {
this.$Notice.error({ title: (this.$t('components.appColumnLink.error') as string), desc: (this.$t('components.appColumnLink.valueItemException') as string) });
return;
}
// 公共参数处理
let data: any = {};
const bcancel: boolean = this.handlePublicParams(data);
if (!bcancel) {
return;
}
// 参数处理
let _context = data.context;
let _param = data.param;
Object.assign(_context, { [this.deKeyField]: this.data[this.valueitem] });
const view = JSON.parse(JSON.stringify(this.linkview));
const viewname2: string = this.$util.srfFilePath2(view.viewname);
view.viewname = viewname2;
if (view.isRedirectView) {
this.openRedirectView($event, view, data);
} else if (Object.is(view.placement, 'INDEXVIEWTAB') || Object.is(view.placement, '')) {
this.openIndexViewTab(view, _context, _param);
} else if (Object.is(view.placement, 'POPOVER')) {
this.openPopOver($event, view, _context, _param);
} else if (Object.is(view.placement, 'POPUPMODAL')) {
this.openPopupModal(view, _context, _param);
} else if (view.placement.startsWith('DRAWER')) {
this.openDrawer(view, _context, _param);
}
}
/**
* 路由模式打开视图
*
* @private
* @param {string} viewpath
* @param {*} data
* @memberof AppColumnLink
*/
private openIndexViewTab(view: any, context: any, param: any): void {
const routePath = this.$viewTool.buildUpRoutePath(this.$route, this.context, view.deResParameters, view.parameters, [context] , param);
this.$router.push(routePath);
}
/**
* 模态模式打开视图
*
* @private
* @param {*} view
* @param {*} data
* @memberof AppColumnLink
*/
private openPopupModal(view: any, context: any, param: any): void {
let container: Subject<any> = this.$appmodal.openModal(view, context, param);
container.subscribe((result: any) => {
if (!result || !Object.is(result.ret, 'OK')) {
return;
}
this.openViewClose(result);
});
}
/**
* 抽屉模式打开视图
*
* @private
* @param {*} view
* @param {*} data
* @memberof AppColumnLink
*/
private openDrawer(view: any, context: any, param: any): void {
let container: Subject<any> = this.$appdrawer.openDrawer(view, context, param);
container.subscribe((result: any) => {
if (!result || !Object.is(result.ret, 'OK')) {
return;
}
this.openViewClose(result);
});
}
/**
* 气泡卡片模式打开
*
* @private
* @param {*} $event
* @param {*} view
* @param {*} data
* @memberof AppColumnLink
*/
private openPopOver($event: any, view: any, context: any, param: any): void {
let container: Subject<any> = this.$apppopover.openPop($event, view, context, param);
container.subscribe((result: any) => {
if (!result || !Object.is(result.ret, 'OK')) {
return;
}
this.openViewClose(result);
});
}
/**
* 独立里面弹出
*
* @private
* @param {string} url
* @memberof AppColumnLink
*/
private openPopupApp(url: string): void {
window.open(url, '_blank');
}
/**
* 打开重定向视图
*
* @private
* @param {*} $event
* @param {*} view
* @param {*} data
* @memberof AppColumnLink
*/
private openRedirectView($event: any, view: any, data: any): void {
this.$http.get(view.url, data).then((response: any) => {
if (!response || response.status !== 200) {
this.$Notice.error({ title: '错误', desc: '请求异常' });
}
if (response.status === 401) {
return;
}
const { data: result } = response;
if (result.viewparams && !Object.is(result.viewparams.srfkey, '')) {
Object.assign(data, { srfkey: result.viewparams.srfkey });
}
if (Object.is(result.openmode, 'POPUPAPP') && result.url && !Object.is(result.url, '')) {
this.openPopupApp(result.url);
} else if (Object.is(result.openmode, 'INDEXVIEWTAB') || Object.is(result.openmode, '')) {
// 所有数据保持在同一级
if (data.srfparentdata) {
Object.assign(data, data.srfparentdata);
delete data.srfparentdata;
}
this.openIndexViewTab(view, null, data);
} else if (Object.is(result.openmode, 'POPUPMODAL')) {
const viewname = this.$util.srfFilePath2(result.viewname);
const view: any = {
viewname: viewname,
title: result.title,
width: result.width,
height: result.height,
}
this.openPopupModal(view, null,data);
} else if (result.openmode.startsWith('DRAWER')) {
const viewname = this.$util.srfFilePath2(result.viewname);
const view: any = {
viewname: viewname,
title: result.title,
width: result.width,
height: result.height,
placement: result.openmode,
}
this.openDrawer(view, null, data);
} else if (Object.is(result.openmode, 'POPOVER')) {
const viewname = this.$util.srfFilePath2(result.viewname);
const view: any = {
viewname: viewname,
title: result.title,
width: result.width,
height: result.height,
placement: result.openmode,
}
this.openPopOver($event, view, null, data);
}
}).catch((response: any) => {
if (!response || !response.status || !response.data) {
this.$Notice.error({ title: '错误', desc: '系统异常!' });
return;
}
if (response.status === 401) {
return;
}
});
}
/**
* 打开页面关闭
*
* @param {*} result
* @memberof AppColumnLink
*/
public openViewClose(result: any) {
let item: any = {};
if (result.datas && Array.isArray(result.datas)) {
Object.assign(item, result.datas[0]);
}
console.log(item);
}
/**
* 公共参数处理
*
* @param {*} arg
* @returns
* @memberof AppColumnLink
*/
public handlePublicParams(arg: any): boolean {
if (!this.data) {
this.$Notice.error({ title: (this.$t('components.appColumnLink.error') as string), desc: (this.$t('components.appColumnLink.rowDataException') as string) });
return false;
}
// 合并表单参数
arg.param = JSON.parse(JSON.stringify(this.viewparams));
arg.context = JSON.parse(JSON.stringify(this.context));
return true;
}
}
</script>
<style lang='less'>
@import './app-column-link.less';
</style>
\ No newline at end of file
......@@ -5,12 +5,12 @@
<input-box :disabled="disabled" v-model="editorvalue" @change="($event)=>{onEditorValueChange($event)}"></input-box>
</template>
<template v-else-if="Object.is(type,'dropdown')">
<i-select v-model="editorvalue" :disabled="disabled" :clearable="true" placeholder='请选择...' >
<i-select v-model="editorvalue" :disabled="disabled" :clearable="true" :placeholder="$t('components.appColumnRender.select')" >
<i-option v-for="(item, index) in codelist" :key="index" :value="item.value">{{item.text}}</i-option>
</i-select>
</template>
<template v-else>
<span>{{type}}不支持</span>
<span>{{type}}{{$t('components.appColumnRender.unsupported')}}</span>
</template>
</div>
</template>
......@@ -20,7 +20,8 @@ import { Vue, Component, Prop, Model, Emit, Watch } from "vue-property-decorator
import { Subject } from "rxjs";
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
@Component({})
@Component({
})
export default class AppColumnRender extends Vue {
/**
* 值
......
......@@ -10,7 +10,7 @@
<el-menu v-show="!filterVal" :unique-opened="true">
<template v-for="(item, index) of list">
<el-submenu :key="item.type + index" :index="item.type + index">
<div slot="title">{{item.name}}</div>
<div slot="title">{{Object.is(item.type, 'app') ? $t('components.appDashboardDesign.global') : item.name}}</div>
<template v-for="(item2, index2) of item.children">
<el-submenu :key="item2.type + index2" :index="item2.type + index2">
<div slot="title">{{item2.name}}</div>
......@@ -225,9 +225,9 @@ export default class AppDashboardDesign extends Vue {
* @memberof AppDashboardDesign
*/
public created() {
this.loadList();
this.load();
if (this.viewState) {
this.loadList();
this.load();
if (this.viewState) {
this.viewStateEvent = this.viewState.subscribe(({ tag, action, data }) => {
if (Object.is('save', action)) {
this.save();
......
.app-data-upload-view{
width: 100%;
height: 100%;
padding: 16px;
.import-temp{
text-align: right;
vertical-align: middle;
color: #409EFF;
}
.data-info-content{
height: 100%;
width: 100%;
overflow: auto;
}
.font-class{
margin: 0px;
padding: 0px;
font-family: '微软雅黑','黑体','宋体';
font-size: 16px;
}
}
\ No newline at end of file
此差异已折叠。
.app-debug-actions {
position: absolute;
top: 0px;
left: 50%;
z-index: 10000;
text-align: center;
transition: all 0.3s;
margin-top: -32px;
>.actions {
.ivu-btn {
width: 100%;
}
}
>.show-buttons {
width: 100%;
height: 18px;
border-width: 0px 1px 1px 1px;
border-style: solid;
border-color: #8893a7;
text-align: center;
color: #8893a7;
cursor: pointer;
>.button {
margin-top: -5px;
}
}
}
.app-debug-actions:hover {
margin-top: 0px;
}
\ No newline at end of file
<template>
<div class="app-debug-actions">
<div class="actions">
<button-group vertical>
<i-button :title="$t('components.appDebugActions.button')" :type="sdc.isShowTool ? 'warning' : 'info'" ghost @click="() => sdc.showToolChange()" :icon="sdc.isShowTool ? 'ios-bug' : 'ios-bug-outline'" ></i-button>
</button-group>
</div>
<div class="show-buttons">
<div class="button"><i class="ivu-icon ivu-icon-ios-arrow-down" /></div>
</div>
</div>
</template>
<script lang = 'ts'>
import { Vue, Component } from 'vue-property-decorator';
import { StudioActionUtil } from '@/utils';
import { Environment } from '@/environments/environment';
/**
* 开发模式控制行为组
*
* @export
* @class AppDebugActions
* @extends {Vue}
*/
@Component({
})
export default class AppDebugActions extends Vue {
/**
* 配置平台操作控制器
*
* @type {StudioActionController}
* @memberof AppDebugActions
*/
public sdc: StudioActionUtil = StudioActionUtil.getInstance();
}
</script>
<style lang = "less">
@import "./app-debug-actions.less";
</style>
\ No newline at end of file
......@@ -11,8 +11,8 @@
</component>
</div>
<template v-if="placeholder">
<div v-if="value" class="app-embed-value">{this.value}</div>
<div v-else class="app-embed-placeholder">{this.placeholder}</div>
<div v-if="value" class="app-embed-value">{{value}}</div>
<div v-else class="app-embed-placeholder">{{placeholder}}</div>
</template>
</div>
<div v-else>{{emptyText}}</div>
......
......@@ -2,17 +2,17 @@
<dropdown v-if="itemLevel === 0" :transfer="true" trigger='click'>
<i-button :disabled="item.disabled">
<i class='fa fa-file-excel-o'></i>
<span class='caption'>{{item.caption}}</span>
<span class='caption'>{{caption}}</span>
</i-button>
<dropdown-menu slot='list'>
<dropdown-item>
<p @click="exportExcel($event, 'maxRowCount')">
{{item.caption}}全部(最大{{item.caption}}{{item.MaxRowCount}})
{{caption}}{{$t('components.appExportExcel.total')}}({{$t('components.appExportExcel.max')}}{{caption}}{{item.MaxRowCount}}{{$t('components.appExportExcel.row')}})
</p>
</dropdown-item>
<dropdown-item>
<p @click="exportExcel($event, 'activatedPage')">
{{this.item.caption}}当前页
{{caption}}{{$t('components.appExportExcel.currentPage')}}
</p>
</dropdown-item>
</dropdown-menu>
......@@ -29,7 +29,8 @@ import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
* @class AppExportExcel
* @extends {Vue}
*/
@Component({})
@Component({
})
export default class AppExportExcel extends Vue {
/**
......@@ -40,6 +41,14 @@ export default class AppExportExcel extends Vue {
*/
@Prop() public item?: any;
/**
* 工具栏项
*
* @type {*}
* @memberof AppExportExcel
*/
@Prop() public caption?: any;
/**
* 工具栏项层级
*
......@@ -128,4 +137,4 @@ export default class AppExportExcel extends Vue {
<style lang='less'>
@import './app-export-excel.less';
</style>
\ No newline at end of file
</style>
.app-file-upload{
.upload-col{
text-align: center;
.button-preview{
padding: 8px 15px 8.5px 15px;
.ivu-badge{
margin-left: 2px;
.ivu-badge-count{
height: auto;
min-width: auto;
line-height: unset;
padding: 0 4px;
}
}
}
}
}
.upload-preview-modal{
.ivu-modal{
height: 86%;
.ivu-modal-content{
height: 100%;
overflow-y: scroll;
.preview-file-list-item{
margin: 0 8px 8px 0;
display: inline-block;
position: relative;
}
.preview-file-list-img{
display: inline-block;
position: relative;
}
.file-name{
text-align: center;
}
.preview-file-list-actions{
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
font-size: 20px;
text-align: center;
color: #fff;
opacity: 0;
transition: opacity .3s;
}
.preview-file-list-actions:hover{
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
font-size: 20px;
text-align: center;
color: #fff;
opacity: 1;
background-color: rgba(0,0,0,.5);
}
.preview-file-list-actions::after{
display: inline-block;
content: "";
height: 100%;
vertical-align: middle;
}
.action-delete{
margin-left:15px;
}
}
}
}
\ No newline at end of file
<template>
<el-upload
:disabled="disabled"
:file-list="files"
:action="uploadUrl"
:headers="{}"
:before-upload="beforeUpload"
:before-remove="onRemove"
:on-success="onSuccess"
:on-error="onError"
:on-preview="onDownload"
:drag="isdrag"
>
<el-button v-if="!isdrag" size='small' icon='el-icon-upload' :disabled="disabled">{{this.$t('app.fileUpload.caption')}}</el-button>
<i v-if="isdrag" class="el-icon-upload"></i>
<div v-if="isdrag" class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>
<div class="app-file-upload">
<el-row>
<el-col v-if="rowPreview && files.length > 0" :span="12" class="upload-col">
<el-button size='small' class="button-preview" icon='el-icon-view' :disabled="disabled" @click="()=>{this.dialogVisible = true;}">{{$t('components.appFileUpload.preview')}}<Badge :count="files.length" type="info"></Badge></el-button>
</el-col>
<el-col :span="(rowPreview && files.length > 0) ? 12 : 24" class="upload-col">
<el-upload
:disabled="disabled"
:file-list="files"
:action="uploadUrl"
:headers="{}"
:before-upload="beforeUpload"
:before-remove="onRemove"
:on-success="onSuccess"
:on-error="onError"
:on-preview="onDownload"
:drag="isdrag"
:show-file-list="!rowPreview"
>
<el-button v-if="!isdrag" size='small' icon='el-icon-upload' :disabled="disabled">{{this.$t('app.fileUpload.caption')}}</el-button>
<i v-if="isdrag" class="el-icon-upload"></i>
<div v-if="isdrag" class="el-upload__text" v-html="$t('components.appFileUpload.uploadText')"></div>
</el-upload>
</el-col>
</el-row>
<modal width="80%" v-model="dialogVisible" footer-hide class-name='upload-preview-modal'>
<ul class="">
<li v-for="(file,index) in files" :key="index" class="preview-file-list-item">
<div class='preview-file-list-img'>
<el-image :src="file.url" class='' style=''>
<div slot='error' class='image-slot'>
<img src="/assets/img/picture.png" style='width:100%;height:100%;'>
</div>
</el-image>
<div class='preview-file-list-actions' @mouseenter="()=>{showActions = true;}" @mouseleave="()=>{showActions = false;}">
<span v-show="showActions" class='action-download'>
<i class='el-icon-download' @click="onDownload(file)"></i>
</span>
<span v-show="showActions" :style="{ 'display': disabled? 'none' : 'inline-block' }" class='action-delete'>
<i class='el-icon-delete' @click="onRemove(file, files)"></i>
</span>
</div>
</div>
<div class="file-name">{{file.name}}</div>
</li>
</ul>
</modal>
</div>
</template>
<script lang="ts">
......@@ -23,7 +55,8 @@ import { Environment } from '@/environments/environment';
import { CreateElement } from 'vue';
import { Subject, Unsubscribable } from 'rxjs';
@Component({})
@Component({
})
export default class AppFileUpload extends Vue {
/**
......@@ -87,12 +120,8 @@ export default class AppFileUpload extends Vue {
if (this.ignorefieldvaluechange) {
return;
}
if (newval) {
this.files = JSON.parse(newval);
this.dataProcess();
} else {
this.files = [];
}
this.setFiles(newval)
this.dataProcess();
}
/**
......@@ -188,6 +217,21 @@ export default class AppFileUpload extends Vue {
*/
public appData: any;
/**
* 设置files
*
* @private
* @memberof AppFileUpload
*/
private setFiles(value:any): void {
let _files = JSON.parse(value);
if (value && Object.prototype.toString.call(_files)=='[object Array]') {
this.files = _files;
} else {
this.files = [];
}
}
/**
* 数据处理
*
......@@ -230,9 +274,7 @@ export default class AppFileUpload extends Vue {
this.formStateEvent = this.formState.subscribe(($event: any) => {
// 表单加载完成
if (Object.is($event.type, 'load')) {
if (this.value) {
this.files = JSON.parse(this.value);
}
this.setFiles(this.value);
this.dataProcess();
}
});
......@@ -271,12 +313,7 @@ export default class AppFileUpload extends Vue {
this.export_keys = export_keys;
this.custom_arr = custom_arr;
if (this.value) {
let _files = JSON.parse(this.value);
if(typeof _files === "object" ){
this.files = _files;
}
}
this.setFiles(this.value);
this.dataProcess();
}
......@@ -298,7 +335,14 @@ export default class AppFileUpload extends Vue {
* @memberof AppFileUpload
*/
public beforeUpload(file: any) {
// console.log('上传之前');
if(this.imageOnly){
const imageTypes = ["image/jpeg" , "image/gif" , "image/png" , "image/bmp"];
const isImage = imageTypes.some((type: any)=> Object.is(type, file.type));
if (!isImage) {
this.$Notice.error({ title: (this.$t('components.appFileUpload.fileTypeErrorTitle') as any) ,desc: (this.$t('components.appFileUpload.fileTypeErrorInfo') as any)});
}
return isImage;
}
}
/**
......@@ -333,7 +377,7 @@ export default class AppFileUpload extends Vue {
* @memberof AppFileUpload
*/
public onError(error: any, file: any, fileList: any) {
this.$Notice.error({ title: '上传失败' });
this.$Notice.error({ title: (this.$t('components.appFileUpload.uploadError') as any) });
}
/**
......@@ -351,6 +395,9 @@ export default class AppFileUpload extends Vue {
}
});
let value: any = arr.length > 0 ? JSON.stringify(arr) : null;
if(arr.length == 0){
this.dialogVisible = false;
}
this.$emit('formitemvaluechange', { name: this.name, value: value });
}
......@@ -364,6 +411,38 @@ export default class AppFileUpload extends Vue {
window.open(file.url);
}
/**
* 是否只支持图片上传
*
* @type {boolean}
* @memberof AppFileUpload
*/
@Prop({default: false}) public imageOnly!: boolean;
/**
* 是否开启行内预览
*
* @type {boolean}
* @memberof AppFileUpload
*/
@Prop({default: false}) public rowPreview!: boolean;
/**
* 是否开启行内预览
*
* @type {boolean}
* @memberof AppFileUpload
*/
public dialogVisible: boolean = false;
/**
* 是否开启行内预览
*
* @type {boolean}
* @memberof AppFileUpload
*/
public showActions: boolean = false;
}
</script>
......
......@@ -13,13 +13,14 @@
@viewdataschange="viewdataschange"
@viewload="viewload">
</component>
<spin v-if="blockUI" class='app-druipart-spin' fix >{{ blockUITipInfo }}</spin>
<spin v-if="blockUI" class='app-druipart-spin' fix >{{ $t('components.appFormDRUIPart.blockUITipInfo') }}</spin>
</div>
</template>
<script lang = 'ts'>
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import { Subject, Unsubscribable } from 'rxjs';
@Component({})
@Component({
})
export default class AppFormDRUIPart extends Vue {
/**
......@@ -183,14 +184,6 @@ export default class AppFormDRUIPart extends Vue {
*/
public blockUI: boolean = false;
/**
* 遮罩提示信息
*
* @type {string}
* @memberof AppFormDRUIPart
*/
public blockUITipInfo: string = '请先保存主数据';
/**
* 是否刷新关系数据
*
......
......@@ -22,7 +22,7 @@
> .el-tabs {
height: 100%;
> .el-tabs__content {
height: calc(100% - 51px);
height: calc(100% - 62px);
> .el-tab-pane {
height: 100%;
overflow: auto;
......
.app-format-data{
display: inline-block;
}
\ No newline at end of file
<template>
<span class="app-format-data">
{{getcurValue()}}
</span>
</template>
<script lang = 'ts'>
import { Component, Vue, Prop } from 'vue-property-decorator';
import moment from "moment";
@Component({})
export default class AppFormatData extends Vue {
/**
* 格式化正则
*
* @type {string}
* @memberof AppFormatData
*/
@Prop({default:'YYYY-MM-DD HH:mm:ss'}) public format?:string;
/**
* 传入数据
*
* @type {*}
* @memberof AppFormatData
*/
@Prop() public data!:any;
/**
* 显示值
*
* @memberof AppFormatData
*/
getcurValue(){
if(this.data){
if(this.format){
if(this.format.indexOf('%1$t') !== -1){
return moment(this.data).format("YYYY-MM-DD HH:mm:ss");
}else{
return moment(this.data).format(this.format);
}
}else{
return this.data;
}
}else{
return "";
}
}
}
</script>
<style lang="less">
@import './app-format-data.less';
</style>
\ No newline at end of file
.app-header-menus {
display: flex;
align-items: center;
height: 100%;
line-height: 20px;
>.app-header-menu-item{
height: 24px;
display: flex;
align-items: center;
margin-right: 16px;
color: #8893a7;
border: 1px solid rgba(0, 0, 0, 0);
cursor: pointer;
}
>.app-header-menu-item.icon {
>.app-header-menu-item-icon {
font-size: 20px;
}
>.app-header-menu-item-img {
height: 20px;
width: 20px;
>img {
height: 100%;
width: 100%;
}
}
}
>.app-header-menu-item.icon:hover{
color: #68758e;
text-decoration: none;
}
>.app-header-menu-item.text {
border-radius: 4px;
border: 1px solid #8893a7;
text-align: center;
font-size: 12px;
padding: 2px 4px;
>.app-header-menu-item-icon {
width: 20px;
height: 20px;
overflow: hidden;
font-size: 16px;
margin-right: 3px;
margin-top: -3px;
}
>.app-header-menu-item-img {
height: 16px;
width: 16px;
margin-right: 3px;
>img {
height: 100%;
width: 100%;
}
}
}
>.app-header-menu-item.text:hover {
color: #107fff;
border: 1px solid #107fff;
}
}
.app-header-menus:hover {
background: none !important;
}
\ No newline at end of file
<template>
<div class="app-header-menus">
<div v-for="(menu,index) in menus" :key="index" class="app-header-menu-item text" @click="openWindow(menu)">
<div class="app-header-menu-item-icon">
<i :class="menu.iconcls" :aria-hidden="true" />
</div>
<div class="app-header-menu-item-text">{{$t(menu.title)}}</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Model, Emit } from "vue-property-decorator";
import { Subject } from "rxjs";
import { Environment } from '@/environments/environment';
@Component({
})
export default class AppHeaderMenus extends Vue {
/**
* 菜单数据
*
* @type {any}
* @memberof AppHeaderMenus
*/
public menus:any = [
{
name: "ibizlab",
title: "components.appHeaderMenus.ibizlab.title",
url: Environment.ibizlabtUrl,
iconcls: 'fa fa-home',
},
{
name: "publishProject",
title: "components.appHeaderMenus.publishProject.title",
url: Environment.PublishProjectUrl,
iconcls: 'fa fa-folder-open-o',
},
{
name: "ibizstudio",
title: "components.appHeaderMenus.ibizstudio.title",
url: `${Environment.StudioUrl}?#/common_slnindex/srfkeys=${Environment.SlnId}/sysdesign_psdevslnsysmodeltreeexpview`,
iconcls: 'fa fa-wrench',
},
{
name: "ibizbbs",
title: "components.appHeaderMenus.ibizbbs.title",
url: Environment.ibizbbstUrl,
iconcls: 'fa fa-comments-o',
},
];
/**
* 触发界面行为
*
* @memberof AppHeaderMenus
*/
public openWindow(menu:any){
window.open(menu.url, '_blank');
}
}
</script>
<style lang='less'>
@import "./app-header-menus.less";
</style>
\ No newline at end of file
/*** BEGIN:图片上传 ***/
.app-picture-preview {
.preview-file-list-item{
margin: 0 8px 8px 0;
display: inline-block;
position: relative;
}
.preview-file-list-img{
display: inline-block;
position: relative;
width: 148px;
height: 148px;
.el-image{
width: 100%;
height: 100%;
.image-slot{
width: 100%;
height: 100%;
}
}
}
.preview-file-list-actions{
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
font-size: 20px;
text-align: center;
color: #fff;
opacity: 0;
transition: opacity .3s;
.action-download{
margin-left: 15px;
}
}
.preview-file-list-actions:hover{
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
font-size: 20px;
text-align: center;
color: #fff;
opacity: 1;
background-color: rgba(0,0,0,.5);
}
.preview-file-list-actions::after{
display: inline-block;
content: "";
height: 100%;
vertical-align: middle;
}
>div{
display: inline;
}
}
.app-image-preview-model {
width: 100%;
height: 100%;
text-align: center;
.ivu-modal{
top: 50%;
transform: translateY(-50%);
display: inline-block;
.ivu-modal-content{
display: inline-block;
.ivu-modal-close{
display: none;
}
.ivu-modal-body{
display: inline-block;
line-height: 0;
.el-image {
width: auto;
height: auto;
line-height: 0;
.image-slot {
width: auto;
height: auto;
line-height: 0;
img{
max-width: 1200px;
max-height: 1600px;
}
}
}
}
}
}
}
/*** END:图片上传 ***/
\ No newline at end of file
<template>
<div class='app-picture-preview'>
<ul class="">
<li v-for="(file,index) in files" :key="index" class="preview-file-list-item">
<div class='preview-file-list-img'>
<el-image :src="file.url" class='' style=''>
<div slot='error' class='image-slot'>
<img src="/assets/img/picture.png" style='width:100%;height:100%;'>
</div>
</el-image>
<div class='preview-file-list-actions'>
<span class='action-preview'>
<i class='el-icon-zoom-in' @click="onPreview(file)"></i>
</span>
<span class='action-download'>
<i class='el-icon-download' @click="onDownload(file)"></i>
</span>
</div>
</div>
</li>
</ul>
<!-- 预览 -->
<modal v-model="dialogVisible" footer-hide width="auto" class-name='app-image-preview-model'>
<el-image src="dialogImageUrl">
<div slot='error' class='image-slot'>
<img src="/assets/img/picture.png">
</div>
</el-image>
</modal>
</div>
</template>
<script lang = 'ts'>
import { Vue, Component, Prop, Watch, Provide } from 'vue-property-decorator';
import { Environment } from '@/environments/environment';
import { Subject, Unsubscribable } from 'rxjs';
@Component({})
export default class AppImagePreview extends Vue {
/**
* 表单状态
*
* @type {Subject<any>}
* @memberof AppImagePreview
*/
@Prop() public formState?: Subject<any>
/**
* 表单状态事件
*
* @private
* @type {(Unsubscribable | undefined)}
* @memberof AppImagePreview
*/
private formStateEvent: Unsubscribable | undefined;
/**
* 初始化值
*
* @type {*}
* @memberof AppImagePreview
*/
@Prop() public value?: any;
/**
* 数据值变化
*
* @param {*} newval
* @param {*} val
* @returns
* @memberof AppImagePreview
*/
@Watch('value')
onValueChange(newval: any, val: any) {
this.setFiles(newval)
}
/**
* 所属表单项名称
*
* @type {string}
* @memberof AppImagePreview
*/
@Prop() public name!: string;
/**
* 上传文件路径
*
* @memberof AppImagePreview
*/
public uploadUrl = Environment.BaseUrl + Environment.UploadFile;
/**
* 下载文件路径
*
* @memberof AppImagePreview
*/
public downloadUrl = Environment.BaseUrl + Environment.ExportFile;
/**
* 文件列表
*
* @memberof AppImagePreview
*/
@Provide() public files = [];
/**
* 设置files
*
* @private
* @memberof AppImagePreview
*/
private setFiles(value:any): void {
let _files = JSON.parse(value);
if (value && Object.prototype.toString.call(_files)=='[object Array]') {
this.files = _files;
this.files.forEach((file: any) => {
let url = `${this.downloadUrl}/${file.id}`;
file.url = url;
});
} else {
this.files = [];
}
}
/**
* vue 生命周期
*
* @memberof AppImagePreview
*/
public created() {
if (this.formState) {
this.formStateEvent = this.formState.subscribe(($event: any) => {
// 表单加载完成
if (Object.is($event.type, 'load')) {
this.setFiles(this.value);
}
});
}
}
/**
* vue 生命周期
*
* @memberof AppImagePreview
*/
public mounted() {
this.setFiles(this.value);
}
/**
* 组件销毁
*
* @memberof AppImagePreview
*/
public destroyed(): void {
if (this.formStateEvent) {
this.formStateEvent.unsubscribe();
}
}
/**
* 下载文件
*
* @param {*} file
* @memberof AppImagePreview
*/
public onDownload(file: any) {
window.open(file.url);
}
/**
* 预览图片地址
*
* @type {string}
* @memberof AppImagePreview
*/
public dialogImageUrl: string = '';
/**
* 是否显示预览界面
*
* @type {boolean}
* @memberof AppImagePreview
*/
public dialogVisible: boolean = false;
/**
* 预览
*
* @param {*} file
* @memberof AppImagePreview
*/
public onPreview(file: any) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
}
</script>
<style lang = "less">
@import './app-image-preview.less';
</style>
\ No newline at end of file
......@@ -3,7 +3,7 @@
<ul class='el-upload-list el-upload-list--picture-card'>
<!-- 绘制缩略图 Start -->
<li v-for="(file,index) in files" :key="index" class='el-upload-list__item is-success'>
<el-image src={file.url} class='el-upload-list__item-thumbnail' style='min-height:100px;min-width:100px;'>
<el-image :src="file.url" class='el-upload-list__item-thumbnail' style='min-height:100px;min-width:100px;'>
<div slot='error' class='image-slot'>
<i class='el-icon-picture-outline'></i>
</div>
......@@ -119,12 +119,8 @@ export default class AppImageUpload extends Vue {
if (this.ignorefieldvaluechange) {
return;
}
if (newval) {
this.files = JSON.parse(newval);
this.dataProcess();
} else {
this.files = [];
}
this.setFiles(newval)
this.dataProcess();
}
/**
......@@ -220,6 +216,21 @@ export default class AppImageUpload extends Vue {
*/
public appData: any="";
/**
* 设置files
*
* @private
* @memberof AppImageUpload
*/
private setFiles(value:any): void {
let _files = JSON.parse(value);
if (value && Object.prototype.toString.call(_files)=='[object Array]') {
this.files = _files;
} else {
this.files = [];
}
}
/**
* 数据处理
*
......@@ -262,10 +273,7 @@ export default class AppImageUpload extends Vue {
this.formStateEvent = this.formState.subscribe(($event: any) => {
// 表单加载完成
if (Object.is($event.type, 'load')) {
if (this.value) {
// console.log(this.value);
this.files = JSON.parse(this.value);
}
this.setFiles(this.value);
this.dataProcess();
}
});
......@@ -303,12 +311,7 @@ export default class AppImageUpload extends Vue {
this.export_keys = export_keys;
this.custom_arr = custom_arr;
if (this.value) {
let _files = JSON.parse(this.value);
if(typeof _files === "object" ){
this.files = _files;
}
}
this.setFiles(this.value);
this.dataProcess();
}
......
<template>
<dropdown trigger='click' on-click="selectLang">
<dropdown trigger='click' @on-click="selectLang">
<span>
{{title}}
<icon size='18' type='md-arrow-dropdown'></icon>
......
......@@ -15,7 +15,8 @@ import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
import { Subject } from 'rxjs';
import { AppModal } from '@/utils';
@Component({})
@Component({
})
export default class AppMpicker extends Vue {
/**
......@@ -150,16 +151,20 @@ export default class AppMpicker extends Vue {
if (this.activeData) {
Object.assign(param, { srfreferdata: this.activeData });
}
// 错误信息国际化
let error: string = (this.$t('components.appMpicker.error') as any);
let miss: string = (this.$t('components.appMpicker.miss') as any);
let requestException: string = (this.$t('components.appMpicker.requestException') as any);
if(!this.service){
this.$Notice.error({ title: '错误', desc: '缺少参数service' });
this.$Notice.error({ title: error, desc: miss+'service' });
} else if(!this.acParams.serviceName) {
this.$Notice.error({ title: '错误', desc: '缺少参数serviceName' });
this.$Notice.error({ title: error, desc: miss+'serviceName' });
} else if(!this.acParams.interfaceName) {
this.$Notice.error({ title: '错误', desc: '缺少参数interfaceName' });
this.$Notice.error({ title: error, desc: miss+'interfaceName' });
} else {
this.service.getItems(this.acParams.serviceName,this.acParams.interfaceName, param).then((response: any) => {
if (!response) {
this.$Notice.error({ title: '错误', desc: '请求异常' });
this.$Notice.error({ title: error, desc: requestException });
} else {
this.items = [...response];
}
......@@ -232,7 +237,8 @@ export default class AppMpicker extends Vue {
_selectItems[index].srfkey = item[this.deKeyField];
});
}
_context = Object.assign(_context, { srfparentdata: { srfparentkey: this.activeData[this.deKeyField] }, selectedData: [..._selectItems], });
_context = Object.assign(_context, { srfparentdata: { srfparentkey: this.activeData[this.deKeyField] }, });
_viewparams = Object.assign(_viewparams,{ selectedData: [..._selectItems]});
let formdata = this.activeData;
const modal: Subject<any> = this.$appmodal.openModal(view, _context, _viewparams)
modal.subscribe((result: any) => {
......
......@@ -26,6 +26,8 @@
overflow:inherit;
}
}
}
}
.select-no-dropdown.el-select-dropdown{
display: none;
}
\ No newline at end of file
......@@ -9,7 +9,7 @@
@input="onInput" @blur="onBlur" style='width:100%;'>
<template v-slot:default="{item}">
<template v-if="item.isNew">
<div v-if="linkview" @click="newAndEdit">创建并编辑...</div>
<div v-if="linkview" @click="newAndEdit">{{$t('components.appPicker.newAndEdit')}}</div>
</template>
<slot v-else name="default" :item="item"></slot>
</template>
......@@ -52,7 +52,8 @@ import { Component, Vue, Prop, Model, Watch } from 'vue-property-decorator';
import { Subject } from 'rxjs';
import { AppModal } from '@/utils';
@Component({})
@Component({
})
export default class AppPicker extends Vue {
/**
......@@ -165,7 +166,7 @@ export default class AppPicker extends Vue {
* @type {string}
* @memberof AppPicker
*/
@Prop() public valueitem?: string;
@Prop() public valueitem!: string;
/**
* 值
......@@ -309,16 +310,20 @@ export default class AppPicker extends Vue {
}
this.inputState = false;
Object.assign(_param, { query: query });
// 错误信息国际化
let error: string = (this.$t('components.appPicker.error') as any);
let miss: string = (this.$t('components.appPicker.miss') as any);
let requestException: string = (this.$t('components.appPicker.requestException') as any);
if(!this.service){
this.$Notice.error({ title: '错误', desc: '缺少参数service' });
this.$Notice.error({ title: error, desc: miss+'service' });
} else if(!this.acParams.serviceName) {
this.$Notice.error({ title: '错误', desc: '缺少参数serviceName' });
this.$Notice.error({ title: error, desc: miss+'serviceName' });
} else if(!this.acParams.interfaceName) {
this.$Notice.error({ title: '错误', desc: '缺少参数interfaceName' });
this.$Notice.error({ title: error, desc: miss+'interfaceName' });
} else {
this.service.getItems(this.acParams.serviceName,this.acParams.interfaceName, _context, _param).then((response: any) => {
if (!response) {
this.$Notice.error({ title: '错误', desc: '请求异常' });
this.$Notice.error({ title: error, desc: requestException });
} else {
this.items = [...response];
}
......@@ -401,6 +406,7 @@ export default class AppPicker extends Vue {
const view = { ...this.pickupView };
let _context = data.context;
let _param = data.param;
_param.selectedData = [{srfkey: this.data[this.valueitem], srfmajortext: this.curvalue }];
// 判断打开方式
if (view.placement && !Object.is(view.placement, '')) {
if (Object.is(view.placement, 'POPOVER')) {
......@@ -505,7 +511,7 @@ export default class AppPicker extends Vue {
private openRedirectView($event: any, view: any, data: any): void {
this.$http.get(view.url, data).then((response: any) => {
if (!response || response.status !== 200) {
this.$Notice.error({ title: '错误', desc: '请求异常' });
this.$Notice.error({ title: (this.$t('components.appPicker.error') as any), desc: (this.$t('components.appPicker.requestException') as any) });
}
if (response.status === 401) {
return;
......@@ -557,7 +563,7 @@ export default class AppPicker extends Vue {
}
}).catch((response: any) => {
if (!response || !response.status || !response.data) {
this.$Notice.error({ title: '错误', desc: '系统异常!' });
this.$Notice.error({ title: (this.$t('components.appPicker.error') as any), desc: (this.$t('components.appPicker.systemException') as any) });
return;
}
if (response.status === 401) {
......@@ -572,11 +578,8 @@ export default class AppPicker extends Vue {
* @memberof AppPicker
*/
public openLinkView($event: any): void {
if (this.disabled) {
return;
}
if (!this.data || !this.valueitem || !this.data[this.valueitem]) {
this.$Notice.error({ title: '错误', desc: '值项异常!' });
console.error({ title: (this.$t('components.appPicker.error') as any), desc: (this.$t('components.appPicker.valueitemException') as any) });
return;
}
// 公共参数处理
......@@ -640,7 +643,7 @@ export default class AppPicker extends Vue {
return true;
}
if (!this.data) {
this.$Notice.error({ title: '错误', desc: '表单数据异常' });
this.$Notice.error({ title: (this.$t('components.appPicker.error') as any), desc: (this.$t('components.appPicker.formdataException') as any) });
return false;
}
// 合并表单参数
......
<template>
<card class="app-portal-design" :dis-hover="true" :padding="0" :bordered="false">
<p slot="title">
自定义门户
{{$t('components.appPortalDesign.customPortal')}}
</p>
<div class="design-toolbar" slot="extra">
<i-button @click="click">保存</i-button>
<i-button @click="click">{{$t('components.appPortalDesign.save')}}</i-button>
</div>
<div class="design-container">
<app-dashboard-design :viewState="viewState" :context="context" :viewparams="viewparams" :utilServiceName="utilServiceName" @save="onSaved"></app-dashboard-design>
......
<template>
<radio-group class="app-radio-group" v-model="value" >
<radio v-for="(_item,index) in items" :key = "index" :label="_item.value" :disabled="isDisabled || _item.disabled">
<span>{{_item.text}}</span>
<span>{{Object.is(codelistType,'STATIC') ? $t('codelist.'+tag+'.'+_item.value) : _item.text}}</span>
</radio>
</radio-group>
</template>
......
<template>
<div class="app-range-date">
<span class="editor-span"></span>
<span class="editor-span">{{$t('components.appRangDate.from')}}</span>
<template v-for="(item, index) in refFormItem">
<span v-if="index > 0" class="editor-space" :key="index">日 0 时 起 至</span>
<span v-if="index > 0" class="editor-space" :key="index">{{$t('components.appRangDate.daystart')}}</span>
<date-picker
:key="index + '-onlydate'"
v-if="Object.is(editorType, 'DATEPICKEREX') || Object.is(editorType, 'DATEPICKEREX_NOTIME') && index == 0"
type="date"
:transfer="true"
:format="valFormat"
placeholder="请选择时间..."
:placeholder="$t('components.appRangDate.placeholder')"
:value="activeData[item]"
:disabled="disabled"
@on-change="startOnValueChange"
......@@ -21,7 +21,7 @@
type="datetime"
:transfer="true"
:format="valFormat"
placeholder="请选择时间..."
:placeholder="$t('components.appRangDate.placeholder')"
:value="activeData[item]"
:disabled="disabled"
@on-change="startOnValueChange"
......@@ -33,7 +33,7 @@
type="date"
:transfer="true"
:format="valFormat"
placeholder="请选择时间..."
:placeholder="$t('components.appRangDate.placeholder')"
:value="activeData[item]"
:disabled="disabled"
@on-change="endOnValueChange"
......@@ -45,12 +45,12 @@
type="datetime"
:transfer="true"
:format="valFormat"
placeholder="请选择时间..."
:placeholder="$t('components.appRangDate.placeholder')"
:value="activeData[item]"
:disabled="disabled"
@on-change="endOnValueChange"
></date-picker>
<span v-if="index > 0" :key="index + '-only'" class="editor-space">日 24 时 止</span>
<span v-if="index > 0" :key="index + '-only'" class="editor-space">{{$t('components.appRangDate.dayend')}}</span>
</template>
</div>
</template>
......@@ -60,7 +60,8 @@ import { Component, Vue, Prop, Model, Watch } from "vue-property-decorator";
import { Subject } from "rxjs";
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
@Component({})
@Component({
})
export default class AppRangDate extends Vue {
/**
* 编辑项名称
......
<template>
<div class="app-range-editor">
<template v-for="(item, index) in refFormItem">
<span v-if="index > 0" class="editor-space" :key="index">~</span>
<span v-if="index > 0" class="editor-space" :key="index+10">~</span>
<date-picker
:key="index"
v-if="Object.is(editorType, 'DATEPICKEREX') || Object.is(editorType, 'DATEPICKEREX_NOTIME')"
type="date"
:transfer="true"
:format="valFormat"
placeholder="请选择时间..."
:placeholder="$t('components.appRangeEditor.placeholder')"
:value="activeData[item]"
:disabled="disabled"
@on-change="onValueChange">
@on-change="(value,type)=>{onValueChange(item,value)}">
</date-picker>
<time-picker
:key="index"
v-else-if="editorType.startsWith('DATEPICKEREX')"
:transfer="true"
:format="valFormat"
placeholder="请选择时间..."
:placeholder="$t('components.appRangeEditor.placeholder')"
:value="activeData[item]"
:disabled="disabled"
@on-change="onValueChange">
@on-change="(value)=>{onValueChange(item,value)}">
</time-picker>
<i-input
<InputNumber
:key="index"
v-else-if="Object.is(editorType, 'NUMBER')"
:value="activeData[item]"
:disabled="disabled"
:placeholder="$t('components.appRangeEditor.input')"
@on-change="(value)=>{onValueChange(item,value)}">
</InputNumber>
<app-span
:key="index"
v-else-if="Object.is(editorType, 'SPAN')"
:value="activeData[item]"
:disabled="disabled">
</app-span>
<el-input
:key="index"
v-else
type="number"
:value="getValue(item)"
:disabled="disabled"
placeholder="请输入"
@on-change="setValue">
</i-input>
:placeholder="$t('components.appRangeEditor.input')"
@input="(value)=>{onValueChange(item,value)}">
</el-input>
</template>
</div>
</template>
......@@ -41,14 +54,15 @@ import { Component, Vue, Prop, Model, Watch } from 'vue-property-decorator';
import { Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
@Component({})
export default class AppPicker extends Vue {
@Component({
})
export default class AppRangeEditor extends Vue {
/**
* 编辑项名称
*
* @type {string}
* @memberof AppPicker
* @memberof AppRangeEditor
*/
@Prop() public name!: string;
......@@ -56,7 +70,7 @@ export default class AppPicker extends Vue {
* 是否禁用
*
* @type {boolean}
* @memberof AppPicker
* @memberof AppRangeEditor
*/
@Prop() public disabled!: boolean;
......@@ -64,7 +78,7 @@ export default class AppPicker extends Vue {
* 表单数据对象
*
* @type {*}
* @memberof AppPicker
* @memberof AppRangeEditor
*/
@Prop() public activeData: any;
......@@ -72,7 +86,7 @@ export default class AppPicker extends Vue {
* 值格式
*
* @type {string}
* @memberof AppPicker
* @memberof AppRangeEditor
*/
@Prop() public format!: string;
......@@ -80,7 +94,7 @@ export default class AppPicker extends Vue {
* 编辑器类型
*
* @type {string}
* @memberof AppPicker
* @memberof AppRangeEditor
*/
@Prop() public editorType!: string;
......@@ -88,7 +102,7 @@ export default class AppPicker extends Vue {
* 关系表单项集合
*
* @type {string[]}
* @memberof AppPicker
* @memberof AppRangeEditor
*/
@Prop() public refFormItem!: string[];
......@@ -105,7 +119,7 @@ export default class AppPicker extends Vue {
* 处理值格式
*
* @readonly
* @memberof AppPicker
* @memberof AppRangeEditor
*/
get valFormat() {
return this.format.replace('YYYY', 'yyyy').replace('DD', 'dd');
......@@ -116,7 +130,7 @@ export default class AppPicker extends Vue {
*
* @param {string} name
* @returns
* @memberof AppPicker
* @memberof AppRangeEditor
*/
public getValue(name: string) {
return this.activeData[name];
......@@ -127,7 +141,7 @@ export default class AppPicker extends Vue {
*
* @param {string} name
* @param {*} val
* @memberof AppPicker
* @memberof AppRangeEditor
*/
public setValue(name: string, val: any) {
this.inputDataChang.next({ name: name, value: val });
......@@ -153,7 +167,7 @@ export default class AppPicker extends Vue {
*
* @param {string} name
* @param {*} value
* @memberof AppPicker
* @memberof AppRangeEditor
*/
public onValueChange(name: string, value: any) {
this.$emit('formitemvaluechange', { name: name, value: value });
......
......@@ -44,6 +44,7 @@ export default class AppRichTextEditor extends Vue {
this.init(newval);
}
}
/**
* 输入name
......@@ -63,17 +64,38 @@ export default class AppRichTextEditor extends Vue {
/**
* 当前语言,默认中文
*/
@Prop() langu?: string;
public langu: any = localStorage.getItem('local') ? localStorage.getItem('local') : 'zh_CN' ;
/**
* 监听语言变化
*/
@Watch('$i18n.locale')
onLocaleChange(newval: any, val: any) {
console.log("语言变更"+newval)
this.langu = newval;
if(this.editor){
tinymceCode.remove('#' + this.id);
}
this.init('');
}
/**
* 语言映射文件
*/
public languMap:any = {
'zh-CN': 'zh_CN',
'en-US': 'en_US',
};
/**
* 上传文件路径
*/
public uploadUrl = '/' + Environment.BaseUrl + Environment.UploadFile;
public uploadUrl = Environment.BaseUrl + Environment.UploadFile;
/**
* 下载路径
*/
public downloadUrl = '/' + Environment.BaseUrl + Environment.ExportFile;
public downloadUrl = Environment.BaseUrl + Environment.ExportFile;
/**
* 当前富文本
......@@ -154,7 +176,8 @@ export default class AppRichTextEditor extends Vue {
paste_data_images: true,
codesample_content_css: 'assets/tinymce/prism.css',
skin_url: './assets/tinymce/skins/lightgray',
language_url: './assets/tinymce/langs/' + (this.langu ? this.langu : 'zh_CN') + '.js',
language_url: './assets/tinymce/langs/' + this.languMap[this.langu] + '.js',
language:this.languMap[this.langu],
setup: (editor: any) => {
this.editor = editor;
editor.on('blur', () => {
......@@ -166,14 +189,15 @@ export default class AppRichTextEditor extends Vue {
const formData = new FormData();
formData.append('file', bolbinfo.blob(), bolbinfo.filename());
const _url = richtexteditor.uploadUrl;
richtexteditor.uploadFile(_url, formData).subscribe((response: any) => {
if (response.ret === 0 && response.files.length > 0) {
const id: string = response.files[0].id;
const url: string = `${richtexteditor.downloadUrl}?fileid=${id}`
richtexteditor.uploadFile(_url, formData).subscribe((file: any) => {
if (file.filename) {
const id: string = file.fileid;
const url: string = `${richtexteditor.downloadUrl}/${id}`
success(url);
}
}, (error: any) => {
console.log(error);
failure('HTTP Error: ' + error.status);
});
},
init_instance_callback: (editor: any) => {
......
.app-span{
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
overflow: hidden;
}
<template>
<span>{{text}}</span>
<codelist v-if="tag" :tag="tag" :value="value" :codelistType="codelistType" :renderMode="renderMode" :valueSeparator="valueSeparator" :textSeparator="textSeparator"></codelist>
<span class="app-span" v-else >{{text}}</span>
</template>
<script lang="ts">
import { Vue, Component, Prop,Watch,Model } from 'vue-property-decorator';
import { Vue, Component, Prop, Watch, Model } from 'vue-property-decorator';
import CodeListService from "@service/app/codelist-service";
@Component({})
export default class DropDownList extends Vue {
/**
* 代码表服务对象
*
* @type {CodeListService}
* @memberof AppSpan
*/
public codeListService:CodeListService = new CodeListService({ $store: this.$store });
/**
* 当前值
......@@ -22,15 +16,51 @@ export default class DropDownList extends Vue {
* @type {*}
* @memberof AppSpan
*/
@Prop() public data?: any;
@Prop() public value?: any;
/**
* 代码表标识
*
* @type {string}
* @memberof AppSpan
*/
@Prop() public tag?: string;
/**
* 代码表类型
*
* @type {string}
* @memberof AppSpan
*/
@Prop() public codelistType?: string;
/**
* 获取或模式
* @type {boolean}
* @memberof SelectPicker
*/
@Prop({default:"STR"}) public renderMode?: string;
/**
* 文本分隔符
* @type {boolean}
* @memberof SelectPicker
*/
@Prop({default:"、"}) public textSeparator?: string;
/**
* 值分隔符
* @type {boolean}
* @memberof SelectPicker
*/
@Prop({default:";"}) public valueSeparator?: string;
/**
* 监控表单属性 data 值
*
* @memberof AppSpan
*/
@Watch('data')
@Watch('value')
onDataChange(newVal: any, oldVal: any) {
if(newVal !== oldVal){
this.load();
......@@ -45,28 +75,12 @@ export default class DropDownList extends Vue {
public text:any = '';
/**
* 代码表标识
* 编辑器类型
*
* @type {string}
* @memberof AppSpan
*/
@Prop() public tag?: string;
/**
* 代码表类型
*
* @type {string}
* @memberof AppSpan
*/
@Prop() public codelistType?: string;
/**
* 代码表
*
* @type {any[]}
* @memberof AppSpan
*/
public items: any[] = [];
@Prop() public editorType?: string;
/**
* vue 生命周期
......@@ -78,47 +92,26 @@ export default class DropDownList extends Vue {
}
/**
* 加载代码表
* 处理数据
*
* @memberof AppSpan
*/
public load(){
if(this.tag && Object.is(this.codelistType,"STATIC")){
const codelist = this.$store.getters.getCodeList(this.tag);
if (codelist) {
this.items = [...JSON.parse(JSON.stringify(codelist.items))];
this.setText();
} else {
console.log(`----${this.tag}----代码表不存在`);
if(!this.value || this.tag){
return; //代码表走codelist组件
} else if(Object.is(this.editorType,'PICTURE') || Object.is(this.editorType,'PICTURE_ONE') || Object.is(this.editorType,'FILEUPLOADER')){
let files: any[] = JSON.parse(this.value);
let names: any[] = [];
if(files.length && files.length > 0){
files.forEach((item:any) => {
names.push(item.name);
});
this.text = names.join(',');
}
}else if(this.tag && Object.is(this.codelistType,"DYNAMIC")){
this.codeListService.getItems(this.tag).then((res:any) => {
this.items = res;
this.setText();
}).catch((error:any) => {
console.log(`----${this.tag}----代码表不存在`);
});
}else{
this.setText();
this.text = this.value;
}
}
/**
* 设置显示值
* @memberof AppSpan
*/
public setText(){
if(this.items.length>0){
let currentItem:any = this.items.find((item:any)=>{
return item.value == this.data;
});
if(currentItem){
this.text = currentItem.label;
}
}else{
this.text = this.data;
}
}
}
</script>
......
<template>
<div class="app-stepper">
<el-input-number :value="currentVal" :disabled="disabled" @change="onChange" :min="min" :max="max" :step="step" label="描述文字"></el-input-number>
<el-input-number :value="currentVal" :disabled="disabled" @change="onChange" :min="min" :max="max" :step="step"></el-input-number>
</div>
</template>
......
......@@ -5,11 +5,11 @@
{{viewTitle}}
</div>
<div class="actions">
<div class="action-item" title="进入当前视图配置界面">
<i-button type="text" ghost @click="configView()">配置</i-button>
<div class="action-item" :title="$t('components.appStudioAction.configTitle')">
<i-button type="text" ghost @click="configView()">{{$t('components.appStudioAction.configButton')}}</i-button>
</div>
<div class="action-item" title="建立当前界面的issues">
<i-button type="text" ghost @click="createIssues()">新建issues</i-button>
<div class="action-item" :title="$t('components.appStudioAction.issueTitle')">
<i-button type="text" ghost @click="createIssues()">{{$t('components.appStudioAction.issueButton')}}</i-button>
</div>
</div>
</div>
......@@ -20,7 +20,8 @@ import { Vue, Component, Inject, Prop } from "vue-property-decorator";
import { Environment } from '@/environments/environment';
import { StudioActionUtil } from '@/utils';
@Component({})
@Component({
})
export default class AppStudioAction extends Vue {
/**
......
<template>
<div class='app-theme'>
<poptip
:title="$t('caption.theme')"
:title="$t('components.appTheme.caption.theme')"
popper-class='app-app-theme'
placement='bottom-end'
:width="Object.is($i18n.locale, 'zh-CN') ? 180 : 240">
:width="Object.is($i18n.locale, 'zh-CN') ? 180 : 250">
<a>
<icon class='app-theme-icon' type='md-settings' :size="22" />
</a>
......@@ -23,7 +23,7 @@
</div>
<div>
<i-form label-position='left'>
<form-item :label="$t('caption.font')">
<form-item :label="$t('components.appTheme.caption.font')">
<i-select
:value="selectFont"
size='small'
......@@ -34,7 +34,7 @@
<i-option
:value="font.value"
:key="font.value">
{{$t(`fontFamilys.${font.label}`)}}
{{$t(`components.appTheme.fontFamilys.${font.label}`)}}
</i-option>
</template>
</i-select>
......@@ -50,32 +50,6 @@
import { Component, Vue } from 'vue-property-decorator';
@Component({
i18n: {
messages: {
'zh-CN': {
caption: {
theme: '主题',
font: '字体',
},
fontFamilys: {
MicrosoftYaHei: '微软雅黑',
SimHei: '黑体',
YouYuan: '幼圆',
},
},
'en-US': {
caption: {
theme: 'Theme',
font: 'Font family',
},
fontFamilys: {
MicrosoftYaHei: 'Microsoft YaHei',
SimHei: 'SimHei',
YouYuan: 'YouYuan',
},
}
}
}
})
export default class AppTheme extends Vue {
......
<template>
<el-timeline class="app-timeline">
<template v-for="(item, index) of items">
<el-timeline-item :key="index" :timestamp="item.name" placement="top">
<template v-for="data of item.items">
<div :key="data.srfkey" class="app-timeline-item-title" @click="onClick(data)">
<card>
<div>{{data.name}}</div>
<div>{{data.actor || data.create_uid_text}}</div>
<div>{{data.time || data.create_date}}</div>
</card>
</div>
</template>
</el-timeline-item>
</template>
</el-timeline>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
@Component({})
export default class AppTimeline extends Vue {
/**
* 数据集
*
* @type {string}
* @memberof AppTimeline
*/
@Prop() datas?: string;
/**
* 数据集对象
*
* @protected
* @type {any[]}
* @memberof AppTimeline
*/
protected items: any[] = [];
/**
* 数据集监听
*
* @param {*} newVal
* @param {*} oldVal
* @memberof AppTimeline
*/
@Watch('datas')
onDatasChange(newVal: any, oldVal: any) {
if(newVal) {
this.prepareData(JSON.parse(newVal))
}
}
/**
* 生命周期
*
* @memberof AppTimeline
*/
public mounted() {
if(this.datas) {
this.prepareData(JSON.parse(this.datas))
}
}
/**
* 准备数据
*
* @param {*} datas
* @memberof AppTimeline
*/
public prepareData(datas: any[] = []) {
let format: any = this.getFormat();
datas.forEach((data: any) => {
let time = data.time || data.create_date;
if(format.test(time)) {
time.replace(format, (str: string) => {
let item: any = this.items.find((item: any) => Object.is(item.name, str));
if(!item) {
item = {name: str, items: [data]};
this.items.push(item);
} else {
item.items.push(data);
}
});
}
})
}
/**
* 获取时间格式
*
* @memberof AppTimeline
*/
public getFormat() {
return /^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})/;
}
/**
* 选中
*
* @param {*} item
* @memberof AppTimeline
*/
public onClick(item: any) {
this.$emit('click', item);
}
}
</script>
<style lang="less">
.app-timeline {
margin-left: 50px;
.el-timeline-item__timestamp {
color: initial;
font-weight: bold;
}
.app-timeline-item-title {
cursor: pointer;
width: 350px;
margin-bottom: 5px;
}
.app-timeline-item-title:hover {
color: #108cee;
}
}
</style>
\ No newline at end of file
<template>
<div v-if="refviewname" class="app-tree-picker">
<Dropdown :visible="visible" trigger="custom" style="left:0px;width: 100%" @on-clickoutside="() => {triggerMenu(false);}" >
<Input v-model="inputValue" class="tree-input" type="text" :placeholder="placeholder" :disabled="disabled" @on-change="OnInputChange" @on-focus="()=>{triggerMenu();}" >
<Input v-model="inputValue" class="tree-input" type="text" :placeholder="placeholder ? placeholder : $t('components.appTreePicker.placeholder')" :disabled="disabled" @on-change="OnInputChange" @on-focus="()=>{triggerMenu();}" >
<template v-slot:suffix>
<i v-if="inputValue && !disabled" class='el-icon-circle-close' @click="onClear"></i>
<Icon :type="visible ? 'ios-arrow-up' : 'ios-arrow-down'" class="icon-arrow" @click="() => {triggerMenu();}"></Icon>
......@@ -13,7 +13,7 @@
:is="refviewname"
:viewdata="viewdata"
:viewparam="viewparam"
:ifShowButtons="false"
:isShowButton="false"
:viewDefaultUsage="false"
@viewdataschange="setValue"
style="height:100%;">
......@@ -28,7 +28,8 @@ import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import { CreateElement } from 'vue';
import { Subject, Subscription } from 'rxjs';
@Component({})
@Component({
})
export default class AppTreePicker extends Vue {
/**
......@@ -118,7 +119,7 @@ export default class AppTreePicker extends Vue {
* @type {string}
* @memberof AppTreePicker
*/
@Prop({default:"请选择..."}) public placeholder!: string;
@Prop() public placeholder?: string;
/**
* 属性项名称
......
.app-upload-file-info{
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
overflow: hidden;
text-align: left;
.file-item{
color: #606266;
display: inline-block;
padding-left: 4px;
text-overflow: ellipsis;
-webkit-transition: color .3s;
transition: color .3s;
white-space: nowrap;
}
.file-item:hover{
color: #409EFF;
cursor: pointer;
}
i{
margin-right: 7px;
}
}
\ No newline at end of file
<template>
<div class="app-upload-file-info">
<a class="file-item" v-for="file in files" @click="() => onDownload(file)"><i class="el-icon-document"></i>{{file.name}}</a>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
import { Environment } from '@/environments/environment';
import { CreateElement } from 'vue';
import { Subject, Unsubscribable } from 'rxjs';
@Component({
})
export default class AppUploadFileInfo extends Vue {
/**
* 初始化值
*
* @type {*}
* @memberof AppUploadFileInfo
*/
@Prop() public value?: any;
/**
* 数据值变化
*
* @param {*} newval
* @param {*} val
* @memberof AppUploadFileInfo
*/
@Watch('value')
onValueChange(newval: any, val: any) {
this.dataProcess();
}
/**
* 所属表单项名称
*
* @type {string}
* @memberof AppUploadFileInfo
*/
@Prop() public name!: string;
/**
* 上传文件路径
*
* @memberof AppUploadFileInfo
*/
public uploadUrl = Environment.BaseUrl + Environment.UploadFile;
/**
* 下载文件路径
*
* @memberof AppUploadFileInfo
*/
public downloadUrl = Environment.BaseUrl + Environment.ExportFile;
/**
* 文件列表
*
* @memberof AppUploadFileInfo
*/
public files = [];
/**
* 数据处理
*
* @private
* @memberof AppUploadFileInfo
*/
private dataProcess(): void {
if(this.value){
let files = JSON.parse(this.value);
if(files.length){
files.forEach((file: any) => {
let url = `${this.downloadUrl}/${file.id}`;
file.url = url;
});
}else{
files = []
}
this.files =files;
}
}
/**
* vue 生命周期
*
* @memberof AppUploadFileInfo
*/
public created() {
this.dataProcess();
}
/**
* 下载文件
*
* @param {*} file
* @memberof AppUploadFileInfo
*/
public onDownload(file: any) {
window.open(file.url);
}
}
</script>
<style lang='less'>
@import './app-upload-file-info.less';
</style>
\ No newline at end of file
......@@ -2,13 +2,13 @@
<div class='app-header-user'>
<dropdown @on-click="userSelect" :transfer="true">
<div class='user'>
<span>{{user.name}}</span>
<span>{{user.name ? user.name : $t('components.appUser.name')}}</span>
&nbsp;&nbsp;<avatar :src="user.avatar" />
</div>
<dropdown-menu class='menu' slot='list' style='font-size: 15px !important;'>
<dropdown-item name='logout' style='font-size: 15px !important;'>
<span><i aria-hidden='true' class='fa fa-cogs' style='margin-right: 8px;'></i></span>
<span>{{$t('user.logout')}}</span>
<span>{{$t('components.appUser.logout')}}</span>
</dropdown-item>
</dropdown-menu>
</dropdown>
......@@ -18,24 +18,6 @@
import { Vue, Component } from 'vue-property-decorator';
@Component({
i18n: {
messages: {
'zh-CN': {
user: {
name: '匿名访问',
logout: '退出登陆',
surelogout: '确认要退出登陆?',
}
},
'en-US': {
user: {
name: 'Anonymous access',
logout: 'Logout',
surelogout: 'Are you sure logout?',
}
}
}
}
})
export default class AppUser extends Vue {
......@@ -45,7 +27,7 @@ export default class AppUser extends Vue {
* @memberof AppUser
*/
public user = {
name: 'user.name',
name: '',
avatar: './assets/img/avatar.png',
}
......@@ -57,7 +39,7 @@ export default class AppUser extends Vue {
*/
public userSelect(data: any) {
if (Object.is(data, 'logout')) {
const title: any = this.$t('user.surelogout');
const title: any = this.$t('components.appUser.surelogout');
this.$Modal.confirm({
title: title,
onOk: () => {
......
<template>
<div class="codelist">
<span v-if="ifEmpty">{{emptytext}}</span>
<span v-if="ifEmpty">{{$t('codelist.'+tag+'.empty')}}</span>
<template v-if="!ifEmpty">
<template v-if="renderMode == 'string'">
<template v-for="(val, index) in items">
{{ index != 0 ? textSeparator : ''}}
<template v-for="(item, index) in val">
{{ index != 0 ? "、" : ''}}
<i v-if="item.iconCls" :class="item.iconCls"></i>
<span :class="item.textCls" :style="{color:item.color}">{{item.text}}</span>
</template>
</template>
</template>
<template v-else>
<template v-for="(item, index) in items">
{{ index != 0 ? textSeparator : ''}}
<i v-if="item.iconCls" :class="item.iconCls"></i>
<span :class="item.textCls" :style="{color:item.color}">{{item.text}}</span>
</template>
</template>
<template v-for="(item, index) in items">
<span>{{ index != 0 ? textSeparator : ''}}</span>
<i v-if="item.iconCls" :class="item.iconCls"></i>
<span :class="item.textCls" :style="{color:item.color}">{{isUseLangres ? $t(item.text) : item.text}}</span>
</template>
</template>
</div>
</template>
......@@ -43,7 +31,7 @@ export default class CodeList extends Vue {
* @type {string}
* @memberof CodeList
*/
@Prop() public srfkey!: string;
@Prop() public tag!: string;
/**
* 代码表类型
......@@ -62,18 +50,11 @@ export default class CodeList extends Vue {
@Prop() public value?: string;
/**
* 空值显示文本
* 获取或模式
* @type {boolean}
* @memberof SelectPicker
*/
@Prop() public emptytext?: string;
/**
* 绘制模式
* @type {boolean}
* @memberof SelectPicker
*/
@Prop() public renderMode?: string;
@Prop({default:"STR"}) public renderMode?: string;
/**
* 文本分隔符
......@@ -90,14 +71,14 @@ export default class CodeList extends Vue {
@Prop({default:";"}) public valueSeparator?: string;
/**
* 是否
* 是否为空
*
* @memberof CodeList
*/
public ifEmpty:boolean = false;
/**
* 显示数据
* 显示数据集合
*
* @type {any[]}
* @memberof CodeList
......@@ -113,6 +94,13 @@ export default class CodeList extends Vue {
this.dataHandle();
}
/**
* 是否使用多语言资源
* @type {boolean}
* @memberof CodeList
*/
public isUseLangres:boolean = false;
/**
* 数据值变化
*
......@@ -133,6 +121,7 @@ export default class CodeList extends Vue {
* @memberof CodeList
*/
private dataHandle(){
this.isUseLangres = false;
let _this = this;
// 空值判断
if(Object.is(this.$util.typeOf(this.value), 'undefined') || Object.is(this.$util.typeOf(this.value), 'null')){
......@@ -142,15 +131,16 @@ export default class CodeList extends Vue {
this.ifEmpty = false;
// 动态代码表处理
if (Object.is(this.codelistType, "DYNAMIC")) {
this.codeListService.getItems(this.srfkey).then((res: any) => {
this.codeListService.getItems(this.tag).then((res: any) => {
let items = res;
_this.setItems(items, _this);
}).catch((error: any) => {
console.log(`----${_this.srfkey}----代码表不存在`);
console.log(`----${_this.tag}----代码表不存在`);
});
// 静态处理
} else if(Object.is(this.codelistType, "STATIC")){
let items = this.$store.getters.getCodeListItems(this.srfkey);
this.isUseLangres = true;
let items = this.$store.getters.getCodeListItems(this.tag);
_this.setItems(items, _this);
}
}
......@@ -167,7 +157,8 @@ export default class CodeList extends Vue {
private setItems(items: any[], _this: any){
if (items) {
let result:any = [];
if(Object.is(this.renderMode,"number")){
if(Object.is(_this.renderMode,"NUM")){
_this.isUseLangres = false;
items.map((_item: any, index: number)=>{
const nValue = parseInt((_this.value as any), 10);
const codevalue = _item.value;
......@@ -175,22 +166,10 @@ export default class CodeList extends Vue {
result.push(_item);
}
});
} else if(Object.is(this.renderMode,"string")){
const arrayValue: Array<any> = (_this.value as any).split(_this.valueSeparator);
arrayValue.map((value: any, index: number) => {
result.push([]);
let values: any[] = Object.is(this.$util.typeOf(value), 'number') ? [value] : [...(value as any).split(this.valueSeparator)];
values.map((val:any ,num: number)=>{
const item = this.getItem(items, val);
if(item){
result[index].push(item);
}
});
});
} else {
let values: any[] = Object.is(this.$util.typeOf(this.value), 'number') ? [this.value] : [...(this.value as any).split(this.valueSeparator)];
let values: any[] = Object.is(_this.$util.typeOf(_this.value), 'NUM') ? [_this.value] : [...(_this.value as any).split(_this.valueSeparator)];
values.map((value:any ,index: number)=>{
const item = this.getItem(items, value);
const item = _this.getItem(items, value);
if(item){
result.push(item);
}
......@@ -198,9 +177,9 @@ export default class CodeList extends Vue {
}
// 设置items
if(result.length != 0){
_this.items = result;
_this.items = result;
}else{
_this.items = [{text:"不匹配"}];
_this.items = [{text:_this.value}];
}
}
}
......@@ -221,13 +200,23 @@ export default class CodeList extends Vue {
return undefined;
}
result = { ...arr[0] };
return result;
if(Object.is(this.codelistType,'STATIC')){
let value = JSON.parse(JSON.stringify(result));
value.text = 'codelist.'+this.tag+'.'+value.value;
return value;
}else{
return result;
}
}
}
</script>
<style lang='less'>
.codelist {
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
overflow: hidden;
}
</style>
\ No newline at end of file
.data-reveal-type {
height: 100%;
width: 100%;
position: relative;
.show-type {
text-align: right;
padding-right: 16px;
position: absolute;
z-index: 1;
right: 0;
top: 2px;
.ivu-btn-group {
.collapse-btn {
padding: 0;
}
}
}
.data-reveal-content {
height: 100%;
overflow: auto;
}
}
\ No newline at end of file
<template>
<div class="data-reveal-type">
<div class='show-type'>
<button-group v-show="!showTypeDir">
<i-button class="collapse-btn" type="primary" @click="clickCollapse('left')">
<icon type="ios-arrow-back"/>
</i-button>
<i-button v-show="showTypeDir"></i-button>
</button-group>
<button-group v-show="showTypeDir">
<el-tooltip content="默认">
<i-button :type="activeType == 'default' ? 'primary' : 'default'" @click="onClickChange('default')">
<i class="fa fa-table" aria-hidden="true"></i>
</i-button>
</el-tooltip>
<el-tooltip content="日历">
<i-button :type="activeType == 'calendar' ? 'primary' : 'default'" @click="onClickChange('calendar')">
<i class="fa fa-calendar" aria-hidden="true"></i>
</i-button>
</el-tooltip>
<el-tooltip content="时间轴">
<i-button :type="activeType == 'timeline' ? 'primary' : 'default'" @click="onClickChange('timeline')">
<i class="fa fa-list-alt" aria-hidden="true"></i>
</i-button>
</el-tooltip>
<i-button class="collapse-btn" type="primary" @click="clickCollapse('right')">
<icon type="ios-arrow-forward"/>
</i-button>
</button-group>
</div>
<div class="data-reveal-content" v-show="Object.is(activeType, 'default')">
<slot></slot>
</div>
<div class="data-reveal-content" v-show="Object.is(activeType, 'calendar')">
<app-calendar :type="activeType" :datas="datas" @click="onClick"></app-calendar>
</div>
<div class="data-reveal-content" v-show="Object.is(activeType, 'timeline')">
<app-timeline :datas="datas" @click="onClick"></app-timeline>
</div>
</div>
</template>
<script lang="ts">
import {Vue, Component, Prop, Watch} from 'vue-property-decorator';
import AppTimeline from '@components/app-timeline/app-timeline.vue';
import AppCalendar from '@components/app-calendar/app-calendar.vue';
@Component({
components: {
AppTimeline,
AppCalendar
}
})
export default class DataRevealType extends Vue {
/**
* 数据集
*
* @type {string}
* @memberof DataRevealType
*/
@Prop() datas?: string;
/**
* 激活类型
*
* @protected
* @type {string}
* @memberof DataRevealType
*/
protected activeType: string = 'default';
/**
* 工具栏伸缩
*
* @protected
* @type {boolean}
* @memberof DataRevealType
*/
public showTypeDir: boolean = false;
/**
* 切换展现类型
*
* @param {string} type
* @memberof DataRevealType
*/
public onClickChange(type: string) {
this.activeType = type;
}
/**
* 选中
*
* @param {*} item
* @memberof DataRevealType
*/
public onClick(item: any) {
this.$emit('click', item);
}
/**
* 点击伸缩
*
* @param {*} type
* @memberof DataRevealType
*/
public clickCollapse(type: string) {
this.showTypeDir = Object.is(type, 'left') ? true : false;
}
}
</script>
<style lang="less">
@import './data-reveal-type.less';
</style>
\ No newline at end of file
<template>
<div class="ibiz-date-picker">
<div class="date-picker-text">保险期限 : 自</div>
<div class="date-picker-text">{{$t('components.dateRange.startText')}}</div>
<el-date-picker
v-model="value"
type="daterange"
:range-separator="rangeSeparatorr"
:start-placeholder="startPlaceholder"
:end-placeholder="endPlaceholder"
:range-separator="rangeSeparatorr ? rangeSeparatorr : $t('components.dateRange.rangeSeparatorr')"
:start-placeholder="startPlaceholder ? startPlaceholder : $t('components.dateRange.startPlaceholder')"
:end-placeholder="endPlaceholder ? endPlaceholder : $t('components.dateRange.endPlaceholder')"
:disabled="disabled"
value-format="yyyy-MM-dd"
@change="change"
:format="format"
></el-date-picker>
<div class="date-picker-text">日 24 时 止</div>
<div class="date-picker-text">{{$t('components.dateRange.endText')}}</div>
</div>
</template>
<script lang = 'ts'>
import { Component, Vue, Model, Prop } from "vue-property-decorator";
@Component({})
@Component({
})
export default class DateRange extends Vue {
/**
* 日期格式
* @type {any}
......@@ -48,7 +43,7 @@ export default class DateRange extends Vue {
* @type {*}
* @memberof DateRange
*/
@Prop({ default: "结束日期" }) public endPlaceholder?: string;
@Prop() public endPlaceholder?: string;
/**
* 中间Placeholder
......@@ -56,7 +51,7 @@ export default class DateRange extends Vue {
* @type {*}
* @memberof DateRange
*/
@Prop({ default: "日 0 时起 至" }) public rangeSeparatorr?: string;
@Prop() public rangeSeparatorr?: string;
/**
* 开始日期Placeholder
......@@ -64,7 +59,7 @@ export default class DateRange extends Vue {
* @type {*}
* @memberof DateRange
*/
@Prop({ default: "开始日期" }) public startPlaceholder?: string;
@Prop() public startPlaceholder?: string;
/**
* 双向绑定值
......
.dropdown-list-dynamic{
display: inline-block;
}
<template>
<i-select
class='dropdown-list-dynamic'
:transfer="true"
v-model="currentVal"
:disabled="disabled === true ? true : false"
:clearable="true"
:filterable="filterable === true ? true : false"
@on-open-change="onClick"
:placeholder="$t('components.dropDownListDynamic.placeholder')">
<i-option v-for="(item, index) in items" :key="index" :value="item.value">{{($t('userCustom.'+tag+'.'+item.value)!== ('userCustom.'+tag+'.'+item.value))?$t('userCustom.'+tag+'.'+item.value) : item.text}}</i-option>
</i-select>
</template>
<script lang="ts">
import { Vue, Component, Watch, Prop, Model } from 'vue-property-decorator';
import CodeListService from "@service/app/codelist-service";
@Component({
})
export default class DropDownListDynamic extends Vue {
/**
* 代码表服务对象
*
* @type {CodeListService}
* @memberof DropDownListDynamic
*/
public codeListService:CodeListService = new CodeListService({ $store: this.$store });
/**
* 额外参数
*
* @type {*}
* @memberof DropDownListDynamic
*/
public otherParam:any;
/**
* 查询参数
* @type {*}
* @memberof DropDownListDynamic
*/
public queryParam:any;
/**
* 当前选中值
* @type {any}
* @memberof DropDownListDynamic
*/
@Model('change') readonly itemValue!: any;
/**
* 代码表标识
*
* @type {string}
* @memberof DropDownListDynamic
*/
@Prop() public tag?: string;
/**
* 代码表类型
*
* @type {string}
* @memberof DropDownListDynamic
*/
@Prop() public codelistType?: string;
/**
* 传入表单数据
*
* @type {*}
* @memberof DropDownListDynamic
*/
@Prop() public data?: any;
/**
* 监听表单数据
*
* @memberof DropDownListDynamic
*/
@Watch('data',{ deep: true })
onDataChange(newVal: any, val: any){
if(newVal){
this.handleOtherParam();
}
}
/**
* 传入额外参数
*
* @type {*}
* @memberof DropDownListDynamic
*/
@Prop() public itemParam?: any;
/**
* 是否禁用
* @type {any}
* @memberof DropDownListDynamic
*
*/
@Prop() public disabled?: any;
/**
* 是否支持过滤
* @type {boolean}
* @memberof DropDownListDynamic
*/
@Prop() public filterable?: boolean;
/**
* 下拉选提示内容
* @type {string}
* @memberof DropDownListDynamic
*/
@Prop() public placeholder?: string;
/**
* 计算属性(当前值)
* @type {any}
* @memberof DropDownListDynamic
*/
set currentVal(val: any) {
const type: string = this.$util.typeOf(val);
val = Object.is(type, 'null') || Object.is(type, 'undefined') ? undefined : val;
this.$emit('change', val);
}
/**
* 获取值对象
*
* @memberof DropDownListDynamic
*/
get currentVal() {
return this.itemValue;
}
/**
* 代码表
*
* @type {any[]}
* @memberof DropDownListDynamic
*/
public items: any[] = [];
/**
* 处理额外参数
*/
public handleOtherParam(){
if(this.itemParam){
this.queryParam = {};
this.otherParam = this.itemParam.parentdata;
if(this.otherParam && Object.keys(this.otherParam).length >0){
Object.keys(this.otherParam).forEach((item:any) =>{
let value: string | null = this.otherParam[item];
if (value && value.startsWith('%') && value.endsWith('%')) {
const key = value.substring(1, value.length - 1);
if (this.data && this.data.hasOwnProperty(key)) {
value = (this.data[key] !== null && this.data[key] !== undefined) ? this.data[key] : null;
} else {
value = null;
}
}
Object.assign(this.queryParam,{[item]:value});
})
}
}
}
/**
* vue 生命周期
*
* @memberof DropDownListDynamic
*/
public created() {
if(this.tag && Object.is(this.codelistType,"STATIC")){
const codelist = this.$store.getters.getCodeList(this.tag);
if (codelist) {
this.items = [...JSON.parse(JSON.stringify(codelist.items))];
} else {
console.log(`----${this.tag}----代码表不存在`);
}
}else if(this.tag && Object.is(this.codelistType,"DYNAMIC")){
this.codeListService.getItems(this.tag,{},this.queryParam).then((res:any) => {
this.items = res;
}).catch((error:any) => {
console.log(`----${this.tag}----代码表不存在`);
});
}
}
/**
* 下拉点击事件
*
* @param {*} $event
* @memberof DropDownListDynamic
*/
public onClick($event:any){
if($event){
if(this.tag && Object.is(this.codelistType,"DYNAMIC")){
this.codeListService.getItems(this.tag,{},this.queryParam).then((res:any) => {
this.items = res;
}).catch((error:any) => {
console.log(`----${this.tag}----代码表不存在`);
});
}
}
}
}
</script>
<style lang='less'>
@import './dropdown-list-dynamic.less';
</style>
\ No newline at end of file
......@@ -8,10 +8,10 @@
:clearable="true"
:filterable="filterable === true ? true : false"
@on-open-change="onClick"
:placeholder="placeholder ? this.placeholder : '请选择'">
:placeholder="$t('components.dropDownListMpicker.placeholder')">
<i-option v-for="(item, index) in items" :key="index" :value="item.value" :label="item.text">
<Checkbox :value = "(currentVal.indexOf(item.value))==-1?false:true">
{{item.text}}
{{Object.is(codelistType,'STATIC') ? $t('codelist.'+tag+'.'+item.value) : item.text}}
</Checkbox>
</i-option>
</i-select>
......@@ -20,8 +20,9 @@
<script lang="ts">
import { Vue, Component, Prop, Model } from 'vue-property-decorator';
import CodeListService from "@service/app/codelist-service";
@Component({})
export default class DropDownList extends Vue {
@Component({
})
export default class DropDownListMpicker extends Vue {
/**
* 代码表服务对象
*
......@@ -53,6 +54,14 @@ export default class DropDownList extends Vue {
*/
@Prop() public codelistType?: string;
/**
* 代码表值分隔符
*
* @type {string}
* @memberof DropDownListMpicker
*/
@Prop({default:';'}) public valueSeparator?: string;
/**
* 是否禁用
* @type {any}
......@@ -83,7 +92,7 @@ export default class DropDownList extends Vue {
set currentVal(val: any) {
const type: string = this.$util.typeOf(val);
val = Object.is(type, 'null') || Object.is(type, 'undefined') ? [] : val;
let value = val.length > 0 ? val.join(',') : '';
let value = val.length > 0 ? val.join(this.valueSeparator) : '';
this.$emit('change', value);
}
......@@ -93,7 +102,7 @@ export default class DropDownList extends Vue {
* @memberof DropDownListMpicker
*/
get currentVal() {
return this.itemValue? this.itemValue.split(','):[];
return this.itemValue? this.itemValue.split(this.valueSeparator):[];
}
/**
......@@ -110,7 +119,6 @@ export default class DropDownList extends Vue {
* @memberof DropDownListMpicker
*/
public created() {
this.currentVal = [];
if(this.tag && Object.is(this.codelistType,"STATIC")){
const codelist = this.$store.getters.getCodeList(this.tag);
if (codelist) {
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
.crm-app-user-info {
width: 100%;
height: 100%;
display: flex;
align-items: center;
cursor: pointer;
>.user-head-sculpture {
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid #8893a7;
font-size: 18px;
font-weight: 900;
text-align: center;
}
>span {
display: inline-block;
margin-left: 3px;
margin-top: 3px;
}
}
\ No newline at end of file
import { Vue, Component } from 'vue-property-decorator';
import './app-user-info.less';
/**
* 用户信息展示
*
* @export
* @class AppUserInfo
* @extends {Vue}
*/
@Component({})
export class AppUserInfo extends Vue {
/**
* 绘制用户信息内容
*
* @returns {*}
* @memberof AppUserInfo
*/
public render(): any {
return <div class="crm-app-user-info">
<div class="user-head-sculpture">
</div>
<span>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAUCAYAAACqJ5zlAAAAN0lEQVQoU2PsmLzCk5Hh/0wGBgaG/wyM6Yydk5c/+s/AIAsSYGRgeIwpgKEFpBQZMJIhMIzcAQAhOjo7JEcKTQAAAABJRU5ErkJggg=="></img>
</span>
</div>;
}
}
\ No newline at end of file
.debug-actions {
position: absolute;
top: 0px;
left: 50%;
z-index: 10000;
text-align: center;
transition: all 0.3s;
margin-top: -32px;
>.actions {
.ivu-btn {
width: 100%;
}
}
>.show-buttons {
width: 100%;
height: 18px;
border-width: 0px 1px 1px 1px;
border-style: solid;
border-color: #8893a7;
text-align: center;
color: #8893a7;
cursor: pointer;
>.button {
margin-top: -5px;
}
}
}
.debug-actions:hover {
margin-top: 0px;
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
import { CalendarViewBase } from './CalendarViewBase';
/**
* 日历导航视图基类
*
* @export
* @class CalendarExpViewBase
* @extends {ViewBase}
*/
export class CalendarExpViewBase extends CalendarViewBase {
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册