提交 d3ed81a5 authored 作者: 姜立玮's avatar 姜立玮 🤼

初始化

上级
流水线 #256 已取消 于阶段
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = false
indent_style = space
indent_size = 2
[{*.ng, *.sht, *.html, *.shtm, *.shtml, *.htm}]
indent_style = space
indent_size = 2
[{*.jhm, *.xslt, *.xul, *.rng, *.xsl, *.xsd, *.ant, *.tld, *.fxml, *.jrxml, *.xml, *.jnlp, *.wsdl}]
indent_style = space
indent_size = 2
[{.babelrc, .stylelintrc, jest.config, .eslintrc, .prettierrc, *.json, *.jsb3, *.jsb2, *.bowerrc}]
indent_style = space
indent_size = 2
[*.svg]
indent_style = space
indent_size = 2
[*.js.map]
indent_style = space
indent_size = 2
[*.less]
indent_style = space
indent_size = 2
[*.vue]
indent_style = space
indent_size = 2
[{.analysis_options, *.yml, *.yaml}]
indent_style = space
indent_size = 2
# 项目名称 (对应后台一级菜单名称)
VUE_APP_PROJECT_NAME=华软项目
# 项目 Html 标签 title
VUE_APP_HTML_TITLE=华软恒信开发平台
# 用户Token Key
VUE_APP_ADMIN_TOKEN=admin.token
# 用户信息 Key
VUE_APP_ADMIN_USER=admin.user
# 配置代理(基本API)
VUE_APP_PROXY=/base
# 配置服务前缀
VUE_APP_BASE_API=/hrhx-boot
# 配置基本URL
VUE_APP_PUBLIC_PATH=/
# 配置打包文件目录
VUE_APP_OUTPUT_DIR=base
# 配置Websocket服务
VUE_APP_WS=/sys/websocket/
# 配置后端服务
VUE_APP_API=http://127.0.0.1:7013
# 配置文件预览地址
VUE_APP_REVIEW_URL=http://192.168.2.105:8012
# *******************************************
# 微服务配置(如需要微服务模式,请配置下面信息!!!)
# *******************************************
# 微应用 publicPath
VUE_APP_MAIN_PUBLIC_PATH=/
# 微应用 路由BASE (最后一个 "/" 不可省略!!!)
VUE_APP_ROUTER_BASE_URL=/base/
# 运行环境 (默认: development)
NODE_ENV=production
# 配置代理(基本API)
VUE_APP_PROXY=
# 配置服务前缀
VUE_APP_BASE_API=/hrhx-boot
# 配置基本URL
VUE_APP_PUBLIC_PATH=/base
# 配置打包文件目录
VUE_APP_OUTPUT_DIR=dist
# 配置Websocket服务
VUE_APP_WS=/sys/websocket/
# 配置后端服务
VUE_APP_API=http://101.201.144.17:80
# 配置文件预览地址
VUE_APP_REVIEW_URL=http://101.201.144.17:8012
# *******************************************
# 微服务配置(如需要微服务模式,请配置下面信息!!!)
# *******************************************
# 主应用 publicPath
VUE_APP_MAIN_PUBLIC_PATH=/portal
# 微应用 路由BASE (最后一个 "/" 不可省略!!!)
VUE_APP_ROUTER_BASE_URL=/portal/base/
.DS_Store
node_modules
/dist
dist-144
src/utils/getPinYin.js
src/components
# local env files
.env.local
.env.*.local
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
module.exports = {
root: true,
env: {
browser: true,
node: true
},
extends: ['plugin:vue/essential', 'plugin:vue/recommended', 'eslint:recommended', '@vue/prettier'],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'generator-star-spacing': 'off',
'no-mixed-operators': 0,
'vue/max-attributes-per-line': [
2,
{
singleline: 5,
multiline: {
max: 1,
allowFirstLine: false
}
}
],
'vue/attribute-hyphenation': 0,
'vue/html-self-closing': 0,
'vue/component-name-in-template-casing': 0,
'vue/html-closing-bracket-spacing': 0,
'vue/singleline-html-element-content-newline': 0,
'vue/no-unused-components': 0,
'vue/multiline-html-element-content-newline': 0,
'vue/no-use-v-if-with-v-for': 0,
'vue/html-closing-bracket-newline': 0,
'vue/no-parsing-error': 0,
'no-console': 0,
'vue/attributes-order': 0
}
}
public/* linguist-vendored
\ No newline at end of file
.DS_Store
node_modules
/dist
dist-114
# local env files
.env.local
.env.*.local
#package.json
#package-lock.json
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
# node-sass 安装步骤:
# 1、校验本地node_modules中是否已安装node-sass,版本是否一致。
# 2、如未安装或版本不符,从npm源安装node-sass本体。
# 3、检测全局缓存和本地中是否有binding.node,如有即跳过安装。
# 4、没有binding.node则从github下载该二进制文件并将其缓存到全局。
# 5、假如binding.node下载失败,则尝试本地编译出该文件。
# 6、将版本信息写到package-lock.json。
# 由此看到,实际上node-sass依赖了一个二进制文件binding.node,从npm源安装完本体后还会从github下载binding.node。
# 因此安装node-sass相关的失败原因有以下几种:
# 原因一:npm源速度慢
# 由于众所周知的国内网络环境,从国内安装官方源的依赖包会很慢。可以将npm源设置成国内镜像源(如淘宝npm)
# 原因二:binding.node源无法访问或速度慢
# node-sass除了npm部分的代码,还会下载二进制文件binding.node,默认源是github,国内访问较慢,特殊时期甚至无法访问。我们也可以将其改成国内源。
# 原因三:node版本与node-sass版本不兼容
# node-sass版本兼容性并不好,老项目中依赖的node-sass很可能已经不兼容新的node,对应版本兼容如下(或参考官方仓库):
# NodeJS Minimum node-sass version Node Module
# Node13 4.13+ 79
# Node12 4.12+ 72
# Node11 4.10+ 67
# Node10 4.9+ 64
# Node8 4.5.3+ 57
# 原因四:缓存中binding.node版本不一致
# 假如本地node版本改了,或在不同机器上运行,node版本不一致,会报类似错误:
# Found bindings for the following environments:
# - Windows 64-bit with Node.js 6.x
# 这是因为原有binding.node缓存跟现node版本不一致。按提示npm rebuild node-sass或清除缓存重新安装即可。
# 原因五:安装失败后重新安装
# 安装失败后重新安装,有可能无权限删除已安装内容,此时npm uninstall node-sass或手动删掉原目录后再安装即可。
# 原因六:提示没有安装python、build失败等
# 假如拉取binding.node失败,node-sass会尝试在本地编译binding.node,过程就需要python。
# npm 镜像源
registry=https://registry.npm.taobao.org/
# binding.node 镜像源 (安装 node-sass 使用)
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
.DS_Store
node_modules
/dist
dist-144
src/utils/getPinYin.js
# local env files
.env.local
.env.*.local
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
FROM nginx:latest
MAINTAINER hrhx
# 传参数据
ARG PROFILE
ARG OUTPUTDIR
# 将dist文件中的内容复制到 `/usr/share/nginx/html/` 这个目录下面
COPY ${OUTPUTDIR}/ /usr/share/nginx/html/${OUTPUTDIR}/
# 用本地配置文件来替换nginx镜像里的默认配置
COPY nginx/nginx-${PROFILE}.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
node {
// 固定常量
def credentialsId = "5fa0e212-cda7-4b2a-8232-f26dbb9d4793"
def git_url = "http://192.168.2.103:8089/jianglw/hrhx-ui-hrproject.git"
// 传入参数(需在jenkins配置)
// def module_name = "hrhx-ui-jk-nginx"
// def service_ip = "192.168.2.105"
// def service_port = "5082"
// def nginx_port = "5084"
// 自定义参数(需跟环境配置一致)
def proxy = "base"
def baseApi = "hrhx-boot"
def publicPath = ""
def outputDir = "base"
stage("Git pull") {
echo "====================================================="
echo " 拉取分支:【${git_branche}】 "
echo "====================================================="
checkout([
$class: "GitSCM",
branches: [[name: "${git_branche}"]],
extensions: [],
userRemoteConfigs: [[credentialsId: "${credentialsId}", url: "${git_url}"]]
])
echo "拉取【${git_branche}】分支成功!"
}
stage("Yarn build") {
sh "node -v"
sh "npm -v"
sh "npm config get registry"
sh "yarn -v"
sh "yarn config get registry"
sh "yarn install"
sh "rm -rf ${outputDir}"
sh "yarn build:dev"
echo "构建基础环境成功!"
}
stage("Nginx conf") {
if(proxy != ""){
proxy = "\\/" + proxy
}
sh "rm -rf nginx/nginx-dev.conf"
sh "cp nginx/nginx.conf nginx/nginx-dev.conf"
sh "sed -i 's/SERVER_IP/${service_ip}/g' nginx/nginx-dev.conf"
sh "sed -i 's/SERVER_PORT/${service_port}/g' nginx/nginx-dev.conf"
sh "sed -i 's/PROXY/${proxy}/g' nginx/nginx-dev.conf"
sh "sed -i 's/BASE_API/${baseApi}/g' nginx/nginx-dev.conf"
sh "sed -i 's/PUBLIC_PATH/${publicPath}/g' nginx/nginx-dev.conf"
sh "sed -i 's/OUTPUTDIR/${outputDir}/g' nginx/nginx-dev.conf"
echo "修改Nginx配置文件成功!"
}
stage("Docker build") {
sh "docker -H tcp://${param_ip_port} rm -f ${module_name} | true"
sh "docker -H tcp://${param_ip_port} rmi -f ${module_name}:latest | true"
sh "docker -H tcp://${param_ip_port} build --build-arg PROFILE=dev --build-arg OUTPUTDIR=${outputDir} -t ${module_name}:latest ."
echo "构建Nginx镜像成功!"
}
stage("Docker run") {
sh "docker -H tcp://${param_ip_port} run -d -p ${nginx_port}:80 --restart=always --name ${module_name} ${module_name}:latest"
echo "容器【${module_name}】正在启动。。。"
}
}
Hrhx-Base
====
Overview
----
基于 [Ant Design of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 实现的 Ant Design Pro Vue 版
Jeecg-boot 的前端UI框架,采用前后端分离框架,提供强大代码生成器的快速开发平台。
前端页面代码和后端功能代码一键生成,不需要写任何代码,保持jeecg一贯的强大!!
#### 前端技术
- 基础框架:[ant-design-vue](https://github.com/vueComponent/ant-design-vue) - Ant Design Of Vue 实现
- JavaScript框架:Vue
- Webpack
- node
- npm
- eslint
- @vue/cli 3.2.1
- [vue-cropper](https://github.com/xyxiao001/vue-cropper) - 头像裁剪组件
- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - Alipay AntV 数据可视化图表
- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation) - antv/g2 封装实现
项目下载和运行
----
- 拉取项目代码
```bash
git clone http://192.168.2.103:8089/hrhx-base/hrhx-base-ui.git
cd hrhx-base-ui
```
- 安装依赖
```
yarn
```
- 开发模式运行
```
yarn serve
```
- 编译项目
```
yarn build
```
其他说明
----
- 项目使用的 [vue-cli3](https://cli.vuejs.org/guide/), 请更新您的 cli
- 关闭 Eslint (不推荐) 移除 `package.json``eslintConfig` 整个节点代码
- 修改 Ant Design 配色,在文件 `vue.config.js` 中,其他 less 变量覆盖参考 [ant design](https://ant.design/docs/react/customize-theme-cn) 官方说明
```ecmascript 6
css: {
loaderOptions: {
less: {
modifyVars: {
/* less 变量覆盖,用于自定义 ant design 主题 */
'primary-color': '#F5222D',
'link-color': '#F5222D',
'border-radius-base': '4px',
},
javascriptEnabled: true,
}
}
}
```
附属文档
----
- [Ant Design Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn)
- [报表 viser-vue](https://viserjs.github.io/demo.html#/viser/bar/basic-bar)
- [Vue](https://cn.vuejs.org/v2/guide)
- [路由/菜单说明](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-jeecg-vue/src/router/README.md)
Node-Sass
---
#### 关于 node-sass 版本问题
> Warning: LibSass and Node Sass are deprecated. While they will continue to receive maintenance releases indefinitely, there are no plans to add additional features or compatibility with any new CSS or Sass features. Projects that still use it should move onto Dart Sass.
> 警告:不推荐使用 LibSass 和 Node Sass。 虽然他们将继续无限期地接收维护版本,但没有计划添加额外的功能或与任何新的 CSS 或 Sass 功能兼容。 仍在使用它的项目应该转移到 Dart Sass。
根据官方文档所言 `node-sass` 虽仍继续维护,但已不推荐使用,应转移到 `Dart Sass`,而 `Dart Sass``Npm` 上名为 `sass`
#### 卸载 `node-sass` 并安装 `sass`
```bash
$ npm uninstall node-sass sass-loader -D
$ npm install sass sass-loader@^10 -D
```
目前 `Node` 的稳定最新版本为 `v16.15.1`,完全兼容 `Sass`,经测试,`Node` 能够兼容到 `v12` 之上。但不兼容 `v13`。建议最低请从 `v14` 开始使用。⚠️ 请注意,`Node` 大版本号请使用 `偶数` 版本号。
#### ⚠️ 注意
使用 `sass` 替换掉 `node-sass` 之后,样式穿透 `/deep/` 将不再生效应改为 `::v-deep` 继续使用。
## **_本项目最终订版版本号:_**
#### **_Node 版本号:_**`v16.15.1`
#### **_yarn 版本号:_**`v1.22.10`
#### **_Npm 版本号:_**`v8.18.0`
⚠️ 注意:Npm安装依赖的时候,需要在 `npm install` 后面增加 `--legacy-peer-deps`。即:需要执行 `npm i --legacy-peer-deps`。 理由是:npm 升级到 7.x以上后,会出现上游依赖冲突;–legacy-peer-deps:安装时忽略所有 peerDependencie,默认使用npm 4-6版本的安装模式,安装过程中是会跳过对等依赖项。
/**
* 由于 Vue CLI 3 不再使用传统的 webpack 配置文件,故 WebStorm 无法识别别名
* 本文件对项目无任何作用,仅作为 WebStorm 识别别名用
* 进入 WebStorm preferences -> Language & Framework -> JavaScript -> Webpack,选择这个文件即可
* */
const resolve = dir => require('path').join(__dirname, dir)
module.exports = {
resolve: {
alias: {
'@': resolve('src')
}
}
}
module.exports = {
presets: ['@vue/app'],
plugins: [
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}
],
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator'
]
}
#!/bin/bash
# Program:
# deploy jenkins
# Version:
# 1.0.0
# History:
# Created on 2020/11/10
# Last modified on 2020/12/19
JENKINS_URL="http://admin:11801a68ed960150796bfa39c0abfc73c7@192.168.2.105:8080/job"
JOB_NAME="前端部署(116测试环境)"
#JOB_NAME="前端部署(118生产环境)"
# 执行Jenkins远程构建
curl -X POST $JENKINS_URL/$JOB_NAME/build?token=123456
# 查看最近一次构建日志(非实时)
#curl $JENKINS_URL/$JOB_NAME/lastBuild/consoleText
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
client_max_body_size 100m;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
#后台服务配置,配置了这个location便可以通过http://域名/hrhx-boot/xxxx 访问
location PROXY/BASE_API {
proxy_pass http://SERVER_IP:SERVER_PORT/BASE_API;#注意宿主机!!!!
proxy_set_header Host SERVER_IP;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#解决Router(mode: 'history')模式下,刷新路由地址不能找到页面的问题
location /PUBLIC_PATH {
alias /usr/share/nginx/html/OUTPUTDIR/;
try_files $uri $uri/ @router;
index index.html index.htm;
}
location @router {
rewrite ^.*$ /PUBLIC_PATH/index.html last;
}
#配置websocket
location /wss/ {
proxy_pass http://SERVER_IP:SERVER_PORT/BASE_API/; #通过配置端口指向部署websocket的项目
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
此差异已折叠。
{
"name": "hrhx-base-ui",
"version": "2.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --mode dev",
"build:dev": "vue-cli-service build --mode dev",
"build:prod": "vue-cli-service build --mode prod",
"lint": "vue-cli-service lint",
"lint:eslint": "eslint --fix --ext \"src/**/*.{vue,less,css,scss}\"",
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
},
"dependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.17.12",
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
"ant-design-vue": "^1.7.7",
"axios": "^0.18.0",
"dat.gui": "^0.7.9",
"dayjs": "^1.11.5",
"default-passive-events": "^2.0.0",
"echarts": "^5.3.2",
"element-ui": "^2.15.10",
"enquire.js": "^2.1.6",
"leaflet": "^1.9.2",
"leaflet-velocity": "^2.1.2",
"leaflet.pm": "^2.2.0",
"lodash": "^4.17.20",
"lodash.pick": "^4.4.0",
"npm": "^6.14.7",
"nprogress": "^0.2.0",
"ol": "^6.15.1",
"ol-wind": "^1.1.1-alpha.2",
"screenfull": "^5.0.2",
"tinymce": "5.3.0",
"vue": "2.6.10",
"vue-cropper": "^0.5.8",
"vue-json-editor": "^1.4.3",
"vue-loader": "^15.9.3",
"vue-ls": "^3.2.0",
"vue-photo-preview": "^1.1.3",
"vue-print-nb": "^1.5.0",
"vue-print-nb-jeecg": "^1.0.9",
"vue-router": "^3.3.4",
"vuex": "^3.5.1",
"vuex-class": "^0.3.1",
"webpack-bundle-analyzer": "^4.6.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.3.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "^3.3.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-standard": "^4.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.1",
"compression-webpack-plugin": "^3.1.0",
"eslint": "^6.8.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^6.2.2",
"html-webpack-plugin": "^4.3.0",
"less": "^3.12.2",
"less-loader": "^4.1.0",
"prettier": "^2.7.1",
"sass": "^1.52.3",
"sass-loader": "10",
"vue-template-compiler": "2.6.10",
"webpack": "^4.44.1"
},
"postcss": {
"plugins": {
"autoprefixer": {
"grid": false
}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
module.exports = {
// 行宽 default:80
printWidth: 120,
// tab 宽度 default:2
tabWidth: 2,
// 使用 tab 键 default:false
useTabs: false,
// 语句行末是否添加分号 default:true
semi: false,
// 是否使用单引号 default:false
singleQuote: true,
// 对象需要引号在加 default:"as-needed"
quoteProps: 'as-needed',
// jsx单引号 default:false
jsxSingleQuote: true,
// 最后一个对象元素加逗号 default:"es5"
trailingComma: 'none',
// 在对象字面量声明所使用的的花括号后({)和前(})输出空格 default:true
bracketSpacing: true,
// (x) => {} 是否要有小括号 default:"always"
arrowParens: 'avoid',
// default:0
rangeStart: 0,
// default:Infinity
rangeEnd: Infinity,
// default:false
insertPragma: false,
// default:false
requirePragma: false,
// 不包装 Markdown text default:"preserve"
proseWrap: 'never',
// HTML空白敏感性 default:"css"
htmlWhitespaceSensitivity: 'strict',
// 在 *.vue 文件中 Script 和 Style 标签内的代码是否缩进 default:false
vueIndentScriptAndStyle: false,
// 末尾换行符 default:"lf"
endOfLine: 'lf',
// default:"auto"
embeddedLanguageFormatting: 'auto'
}
此差异已折叠。
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>HRHX-基础模板</title>
<link rel="icon" href="<%= BASE_URL %>logo.png">
<script src="<%= BASE_URL %>cdn/babel-polyfill/polyfill_7_2_5.js"></script>
<!-- 将 CDN 转移到本地,防止 CDN 挂掉或者用户程序安装在内网中使用无法访问到 CDN 的情况!!! -->
<style>
html,
body,
#app {
height: 100%;
margin: 0;
padding: 0;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999999;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 120px;
height: 120px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
/* COLOR 1 */
border-top-color: #FFF;
-webkit-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-moz-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 2s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
z-index: 1001;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
/* COLOR 2 */
border-top-color: #FFF;
-webkit-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-moz-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 3s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #FFF;
/* COLOR 3 */
-moz-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-webkit-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 1.5s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg);
/* IE 9 */
transform: rotate(0deg);
/* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg);
/* IE 9 */
transform: rotate(360deg);
/* Firefox 16+, IE 10+, Opera */
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg);
/* IE 9 */
transform: rotate(0deg);
/* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg);
/* IE 9 */
transform: rotate(360deg);
/* Firefox 16+, IE 10+, Opera */
}
}
#loader-wrapper .loader-section {
position: fixed;
top: 0;
width: 51%;
height: 100%;
background: #1890ff;
/* Old browsers */
z-index: 1000;
-webkit-transform: translateX(0);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(0);
/* IE 9 */
transform: translateX(0);
/* Firefox 16+, IE 10+, Opera */
}
#loader-wrapper .loader-section.section-left {
left: 0;
}
#loader-wrapper .loader-section.section-right {
right: 0;
}
/* Loaded */
.loaded #loader-wrapper .loader-section.section-left {
-webkit-transform: translateX(-100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(-100%);
/* IE 9 */
transform: translateX(-100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader-wrapper .loader-section.section-right {
-webkit-transform: translateX(100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(100%);
/* IE 9 */
transform: translateX(100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader {
opacity: 0;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.loaded #loader-wrapper {
visibility: hidden;
-webkit-transform: translateY(-100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateY(-100%);
/* IE 9 */
transform: translateY(-100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.3s 1s ease-out;
transition: all 0.3s 1s ease-out;
}
/* JavaScript Turned Off */
.no-js #loader-wrapper {
display: none;
}
.no-js h1 {
color: #222222;
}
#loader-wrapper .load_title {
font-family: 'Open Sans';
color: #FFF;
font-size: 14px;
width: 100%;
text-align: center;
z-index: 9999999999999;
position: absolute;
top: 60%;
opacity: 1;
line-height: 30px;
}
#loader-wrapper .load_title span {
font-weight: normal;
font-style: italic;
font-size: 14px;
color: #FFF;
opacity: 0.5;
}
/* 滚动条优化 start */
::-webkit-scrollbar {
/*width: 8px;*/
/*height: 8px;*/
/*display: none;*/
}
::-webkit-scrollbar-track {
background: #f6f6f6;
border-radius: 2px;
}
::-webkit-scrollbar-thumb {
background: #cdcdcd;
border-radius: 2px;
}
::-webkit-scrollbar-thumb:hover {
background: #747474;
}
::-webkit-scrollbar-corner {
background: #f6f6f6;
}
/* openlayers overviewmap 设置 */
.map .ol-custom-overviewmap,
.map .ol-custom-overviewmap.ol-uncollapsible {
bottom: auto;
left: auto;
right: 1rem;
bottom: 2.5rem;
}
.map .ol-custom-overviewmap:not(.ol-collapsed) {
border: 1px solid black;
}
.map .ol-custom-overviewmap .ol-overviewmap-map {
border: none;
width: 300px;
}
.map .ol-custom-overviewmap .ol-overviewmap-box {
border: 2px solid red;
}
.map .ol-custom-overviewmap:not(.ol-collapsed) button {
top: auto;
left: auto;
right: 1px;
bottom: 1px;
}
.map .ol-rotate {
top: 170px;
right: 0;
}
.map .ol-mouse-position {
bottom: 1rem;
right: 1rem;
}
/* 滚动条优化 end */
</style>
</head>
<body>
<!-- built files will be auto injected -->
<div id="app">
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载 HRHX-基础项目模板,请耐心等待...</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
<template>
<a-config-provider :locale="locale">
<div id="app">
<router-view />
</div>
</a-config-provider>
</template>
<script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import enquireScreen from '@/utils/device'
export default {
data() {
return {
locale: zhCN
}
},
created() {
let that = this
enquireScreen(deviceType => {
// tablet
if (deviceType === 0) {
that.$store.commit('TOGGLE_DEVICE', 'mobile')
that.$store.dispatch('setSidebar', false)
}
// mobile
else if (deviceType === 1) {
that.$store.commit('TOGGLE_DEVICE', 'mobile')
that.$store.dispatch('setSidebar', false)
} else {
that.$store.commit('TOGGLE_DEVICE', 'desktop')
that.$store.dispatch('setSidebar', true)
}
})
}
}
</script>
<style>
#app {
height: 100vh;
}
</style>
import { axios } from '@/http/http.request'
// get
export function getAction(url, parameter) {
return axios({
url: url,
method: 'get',
params: parameter
})
}
// post
export function postAction(url, parameter) {
return axios({
url: url,
method: 'post',
data: parameter
})
}
// put
export function putAction(url, parameter) {
return axios({
url: url,
method: 'put',
data: parameter
})
}
// delete
export function deleteAction(url, parameter) {
return axios({
url: url,
method: 'delete',
params: parameter
})
}
// post method= { post | put }
export function httpAction(url, parameter, method) {
return axios({
url: url,
method: method,
data: parameter
})
}
/**
* 下载文件 用于excel导出
* @param url
* @param parameter
* @returns {*}
*/
export function downFile(url, parameter) {
return axios({
url: url,
params: parameter,
method: 'get',
responseType: 'blob'
})
}
/**
* 下载文件 用于excel导出
* @param url
* @param parameter
* @returns {*}
*/
export function downFilePost(url, data) {
return axios({
url: url,
data: data,
method: 'post',
responseType: 'blob'
})
}
export const test = '/xxx/xxx/xxx'
import { getAction } from '@/api/manage'
// 用户权限
export const queryPermissionsByUser = params => getAction('/sys/permission/getUserPermissionByToken', params)
// 查询当前登陆人所有发送失败的消息,并发送
export const sendMissMessage = params => getAction('/sys/sysAnnouncementSend/sendMissMessage', params)
// 字典标签专用(通过code获取字典数组)
export const ajaxGetDictItems = (code, params) => getAction(`/sys/dict/getDictItems/${code}`, params)
import { getAction, postAction, putAction,deleteAction } from '@/api/manage'
/* --------- 项目信息 --------- */
const RECORD = '/record'
//查询项目信息
const selectRecordList = data => postAction(`${RECORD}/selectRecordList`,data)
//查询项目信息分页
const selectRecordListPage =`${RECORD}/selectRecordListPage`
//根据id项目信息删除
const removeRecord =`${RECORD}/removeRecord`
const insertRecord = data => postAction(`${RECORD}/insertRecord`, data);
const updateRecord = data => putAction(`${RECORD}/updateRecord`, data);
export{
selectRecordList,
selectRecordListPage,
removeRecord,
insertRecord,
updateRecord
}
\ No newline at end of file
import { axios } from '@/http/http.request'
export function login(parameter) {
return axios({
url: '/sys/login',
method: 'post',
data: parameter
})
}
export function phoneLogin(parameter) {
return axios({
url: '/sys/phoneLogin',
method: 'post',
data: parameter
})
}
export function logout(logoutToken) {
return axios({
url: '/sys/logout',
method: 'post',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'X-Access-Token': logoutToken
}
})
}
/** 清除 a-input 默认样式 **/
.form-descriptions-word .ant-input {
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .ant-input-group-addon {
width: 100%;
border: 0 !important;
box-shadow: none !important;
background-color: #fff;
}
.form-descriptions-word .ant-input-password {
width: 100%;
height: 100%;
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .ant-input-group-addon {
border: 0;
background-color: #fff;
}
/** 清除 a-select 默认样式 **/
.form-descriptions-word .ant-select-selection {
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .required-span-input {
margin-left: -20px;
float: left;
color: #f5222d;
padding-top: 10px;
}
.form-descriptions-word .required-span-textarea {
margin-left: -20px;
float: left;
color: #f5222d;
padding-top: 84px;
}
.form-descriptions-word .ant-descriptions-bordered.ant-descriptions-small .ant-descriptions-item-content {
padding: 0;
}
.form-descriptions-word .ant-form-item {
margin: 0 !important;
}
.form-descriptions-word .ant-input[disabled] {
background-color: #ffffff;
}
.form-descriptions-word .ant-input:hover,
textarea.ant-input:hover {
border-color: white;
}
.form-descriptions-word .ant-select-selection {
border: none;
}
.form-descriptions-word textarea.ant-input {
height: auto;
margin-bottom: 0;
padding: 10px 11px;
}
.form-descriptions-word .ant-descriptions-item-label.ant-descriptions-item-colon {
width: 180px;
text-align: center;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-item-label {
background-color: #fafafa !important;
border-right: 1px solid #e8e8e8 !important;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-row {
border: 1px solid #e8e8e8 !important;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-item-content {
border-right: 1px solid #e8e8e8 !important;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-view {
border: 0;
padding: 1px;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-view table {
border-style: hidden;
box-shadow: 0 0 0 1px #e8e8e8;
border-radius: 8px;
overflow: hidden;
}
/** form 校验信息样式 **/
.form-descriptions-word .ant-form-explain {
margin-left: 10px;
}
.form-descriptions-word {
text-align: left;
}
.form-descriptions-word .ant-descriptions-item-content {
position: relative !important;
}
.form-descriptions-word .triangle-top-left {
position: absolute;
top: 1px;
left: 1px;
width: 0;
height: 0;
border-top: 10px solid #f5222d;
border-right: 10px solid transparent;
z-index: 9;
}
.rule-relative {
position: relative;
}
.rule-relative .triangle-top-left {
position: absolute;
top: 1px;
left: 1px;
width: 0;
height: 0;
border-top: 10px solid #f5222d;
border-right: 10px solid transparent;
z-index: 9;
}
.item-full {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 9;
}
此差异已折叠。
此差异已折叠。
样式风格统一
==========
创建时间:`2020-04-11``邹英豪` <`zouyh@sspss.com.cn`>
src/assets/less/common.less
===========================
#### 定义全局统一样式类名
- `px` 像素大小以5的倍数增长,目前只定义到`50px`,如有需要,可自行定义。
- `mt`:代表 `margin-top` 的样式。例如:`mt5``style="margin-top: 5px;`"
- `mr`:代表 `margin-right` 的样式。例如:`mr5``style="margin-right: 5px;`"
- `mb`:代表 `margin-bottom` 的样式。例如:`mb5``style="margin-bottom: 5px;`"
- `ml`:代表 `margin-left` 的样式。例如:`ml5``style="margin-left: 5px;`"
查询区域按钮样式
=============
- 页面查询字段表单,默认 `Grid` 布局样式给 `<a-col :span="6">`
- 当查询区域有 `展开` 按钮的时候,请根据要查询的表单字段个数合理安排页面布局样式。
- 页面顶部查询区域的按钮(`查询``重置`[ `展开` ]), 按钮之间的间隔为`10px`
例如:`<a-button class="mr10">查询<a-button>` 或者 `<a-button class="ml10">重置<a-button>`
- 点击删除按钮,会有一个弹出层 `Popconfirm` 气泡确认框,在 `<a-icon>` 中使用 `class="dust-red"` 这个类的样式。
更多颜色样式,请参考 [Ant Design 色彩](https://ant.design/docs/spec/colors-cn)
`注意事项1`:由于查询区域是使用 `form` 进行布。
所以建议当按钮小于等于两个的时候,`Grid` 布局的样式建议给 `<a-col :span="6">`
当按钮为三个的时候,`Grid` 布局的样式建议给 `<a-col :span="9">` 具体根据页面样式进行合理布局。
`注意事项2`:一定不要给第一个按钮加 `class="ml10"` 或者最后一个按钮加 `class="mr10"` 的类,
要确保第一个按钮和最后一个按钮前后是没有间距的,防止按钮多的时候,小屏幕的电脑会产生样式错位的问题。
可选 Table 列表
=============
`Table` 可选的时候,在 `Table` 组件上方会有显示 `已选择 xx项``清空`
- 之前写法
```vue
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
<i class="anticon anticon-info-circle ant-alert-icon"></i>
已选择&nbsp;<a style="font-weight: 600">{{selectedRowKeys.length }}</a>&nbsp;&nbsp;
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
```
- 现在写法
```vue
<div class="table-detail">
已选择&nbsp;<a>{{selectedRowKeys.length }}</a>
<a class="ml20" @click="onClearSelected">清空</a>
</div>
```
Tabs 标签页
==========
选项卡切换组件,要切换的内容与标签之间的上下间隔为 `15px`
```vue
<a-tab-pane key="1" tab="标签名" class="mt15"></a-tab-pane>
```
src/utils/commonUtils
=====================
封装公共方法 (已将该文件绑定到 `Vue` 实例上)
- `requestResult.js`:处理后端接口返回的参数信息。
```javascript
<!-- 使用方法 -->
Http('/api/interface').then(res => {
let result = this.$utils.requestResult(res);
if (!result) return;
// 接口请求成功之后的正常逻辑处理...
});
```
- `isEffectiveCommon.js`:格式化页面空数据展示,会将后端返回是
`""` || `undefined` || `"undefined"` || `null` 的数据进行格式化,
在前端页面将其显示为 `-`
```vue
<!-- 使用方法 -->
// 1、在页面上直接显示
<p>{{ $utils.isEffectiveCommon("显示的字段") }}</p>
// 2、在 js 逻辑中进行处理。例如:Table列表字段
columns: [
{
title: '订单号',
align: 'center',
dataIndex: 'orderCode',
customRender: (text, render, index) => {
return this.$utils.isEffectiveCommon(text);
}
}
]
```
此差异已折叠。
.fs12 {
font-size: 12px;
}
.flex {
display: flex;
}
.flex-justify {
display: flex;
justify-content: space-between;
}
/* form 表单样式 */
.left-form-2 .ant-form-item-label-left {
width: 55px;
}
.left-form-2 .ant-form-item-label {
width: 55px;
}
.left-form-2 .ant-form-item-control-wrapper {
width: calc(100% - 55px);
}
.left-form-3 .ant-form-item-label-left {
width: 70px;
}
.left-form-3 .ant-form-item-label {
width: 70px;
}
.left-form-3 .ant-form-item-control-wrapper {
width: calc(100% - 70px);
}
.tree-form .ant-form-item-label-left {
width: 70px;
}
.tree-form .ant-form-item-label {
width: 70px;
}
.tree-form .ant-form-item-control-wrapper {
width: calc(100% - 70px);
}
.left-form-4 .ant-form-item-label-left {
width: 85px;
}
.left-form-4 .ant-form-item-label {
width: 85px;
}
.left-form-4 .ant-form-item-control-wrapper {
width: calc(100% - 85px);
}
.left-form-5 .ant-form-item-label-left {
width: 100px;
}
.left-form-5 .ant-form-item-label {
width: 100px;
}
.left-form-5 .ant-form-item-control-wrapper {
width: calc(100% - 100px);
}
.left-form-6 .ant-form-item-label-left {
width: 115px;
}
.left-form-6 .ant-form-item-label {
width: 115px;
}
.left-form-6 .ant-form-item-control-wrapper {
width: calc(100% - 115px);
}
/* form-info 表单详情 */
.form-info {
padding: 10px 15px;
background-color: #f6f6f6;
}
.info {
display: flex;
}
.info p {
margin-bottom: 0;
}
.info-2 p:first-child {
width: calc(2em + 15px);
}
.info-2 p:last-child {
width: calc(100% - 2em - 15px);
}
.info-3 p:first-child {
width: calc(3em + 15px);
}
.info-3 p:last-child {
width: calc(100% - 3em - 15px);
}
.info-4 p:first-child {
width: calc(4em + 15px);
}
.info-4 p:last-child {
width: calc(100% - 4em - 15px);
}
.info-5 p:first-child {
width: calc(5em + 15px);
}
.info-5 p:last-child {
width: calc(100% - 5em - 15px);
}
.info-6 p:first-child {
width: calc(6em + 15px);
}
.info-6 p:last-child {
width: calc(100% - 6em - 15px);
}
.form-info-wrap {
padding: 10px 15px 0 15px;
background-color: #f6f6f6;
}
.info-wrap {
display: flex;
}
.info-wrap-wrap-2 p:first-child {
text-align: right;
margin-right: 5px;
width: calc(2em + 15px);
}
.info-wrap-wrap-2 p:last-child {
text-align: left;
width: calc(100% - 2em - 20px);
p {
width: 100%;
text-align: left;
margin-bottom: 0;
}
img {
width: 160px;
height: 90px;
}
}
.info-wrap-3 p:first-child {
text-align: right;
margin-right: 5px;
width: calc(3em + 15px);
}
.info-wrap-3 p:last-child {
text-align: left;
width: calc(100% - 3em - 20px);
p {
width: 100%;
text-align: left;
margin-bottom: 0;
}
img {
width: 160px;
height: 90px;
}
}
.info-wrap-4 p:first-child {
text-align: right;
margin-right: 5px;
width: calc(4em + 15px);
}
.info-wrap-4 p:last-child {
text-align: left;
width: calc(100% - 4em - 20px);
p {
width: 100%;
text-align: left;
margin-bottom: 0;
}
img {
width: 160px;
height: 90px;
}
}
.info-wrap-5 p:first-child {
text-align: right;
margin-right: 5px;
width: calc(5em + 15px);
}
.info-wrap-5 p:last-child {
text-align: left;
width: calc(100% - 5em - 20px);
p {
width: 100%;
text-align: left;
margin-bottom: 0;
}
img {
width: 160px;
height: 90px;
}
}
.info-wrap-6 p:first-child {
text-align: right;
margin-right: 5px;
width: calc(6em + 15px);
}
.info-wrap-6 p:last-child {
text-align: left;
width: calc(100% - 6em - 20px);
p {
width: 100%;
text-align: left;
margin-bottom: 0;
}
img {
width: 160px;
height: 90px;
}
}
.info-form-wrap {
padding: 10px 15px 0 15px;
}
.info-form-2 p:first-child {
width: calc(2em + 30px);
}
.info-form-2 p:last-child {
text-align: left;
width: calc(100% - 2em - 30px);
}
.info-form-3 p:first-child {
width: calc(3em + 30px);
}
.info-form-3 p:last-child {
text-align: left;
width: calc(100% - 3em - 30px);
}
.info-form-4 p:first-child {
width: calc(4em + 30px);
}
.info-form-4 p:last-child {
text-align: left;
width: calc(100% - 4em - 30px);
}
.info-form-5 p:first-child {
width: calc(5em + 30px);
}
.info-form-5 p:last-child {
text-align: left;
width: calc(100% - 5em - 30px);
}
.info-form-6 p:first-child {
width: calc(6em + 30px);
}
.info-form-6 p:last-child {
text-align: left;
width: calc(100% - 6em - 30px);
}
/**
* 列表查询通用样式,移动端自适应
*/
.search{
margin-bottom: 54px;
}
.fold{
width: calc(100% - 216px);
display: inline-block
}
.operator{
margin-bottom: 18px;
}
@media screen and (max-width: 900px) {
.fold {
width: 100%;
}
}
.operator button {
margin-right: 5px;
}
i {
cursor: pointer;
}
.trcolor{
background-color: rgba(255, 192, 203, 0.31);
color:red;
}
.table-edit .ant-table-tbody .ant-table-row td {
padding: 0 !important;
}
.table-edit .ant-input {
border: 0 !important;
box-shadow: none !important;
}
\ No newline at end of file
.theme-type-primary {
color: @theme-type-primary;
}
.theme-type-primary-light {
color: @theme-type-primary-light;
}
.text-black-color {
color: @text-black-color;
}
.text-gray-color {
color: @text-gray-color;
}
.text-green-color {
color: @text-green-color;
}
.text-red-color {
color: @text-red-color;
}
.icon-gray-color {
color: @icon-gray-color;
}
.icon-red-color {
color: @icon-red-color;
}
.border-gray-color {
color: @border-gray-color;
}
/* echart图表样式 */
.echart-background-color {
background-color: @echart-background-color;
padding-top: 20px;
border: 1px solid @echart-border-color;
}
\ No newline at end of file
/* 项目主色调 */
@theme-type-primary: #1890FF;
/* 标题栏颜色 */
@theme-type-primary-light: #c5e3ff;
/* 文字颜色 */
@text-black-color: #333333;
@text-gray-color: #BBBBBB;
@text-green-color: #70B603;
@text-red-color: #F91D1D;
/* icon 颜色 */
@icon-gray-color: #BBBBBB;
@icon-red-color: #F91D1D;
/* 表格边框颜色 */
@border-gray-color: #D7D7D7;
/* 表格边框颜色 */
@border-gray-color: #f3f3f3;
/* echart图表背景色 */
@echart-background-color: #f7f7f7;
/* echart图表边框线颜色 */
@echart-border-color: #d7d7d7;
\ No newline at end of file
.form-descriptions-word .title {
width: 100%;
text-align: center;
font-size: 24px;
font-weight: bold;
margin-bottom: 10px;
}
/* 清除 a-input 默认样式 */
.form-descriptions-word .ant-input {
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .ant-input-number {
width: 100%;
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .ant-input-password {
width: 100%;
height: 100%;
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .ant-input-group-addon {
border: 0;
background-color: #fff;
}
/* 清除 a-select 默认样式 */
.form-descriptions-word .ant-select-selection {
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .required-span-input {
margin-left: -20px;
float: left;
color: red;
padding-top: 10px;
}
.form-descriptions-word .required-span-textarea {
margin-left: -20px;
float: left;
color: red;
padding-top: 84px;
}
.form-descriptions-word .ant-descriptions-bordered.ant-descriptions-small .ant-descriptions-item-content {
padding: 0;
}
.form-descriptions-word .ant-form-item {
margin: 0 !important;
}
.form-descriptions-word .ant-input[disabled] {
background-color: #ffffff;
}
.form-descriptions-word .ant-input:hover, textarea.ant-input:hover {
border-color: white;
}
.form-descriptions-word .ant-select-selection {
border: none;
}
.form-descriptions-word textarea.ant-input {
height: auto;
margin-bottom: 0;
padding: 10px 11px;
}
.form-descriptions-word .ant-descriptions-item-label.ant-descriptions-item-colon {
width: 180px;
text-align: center;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-item-label {
/*background-color: #fff;*/
/*border-right: 1px solid #000c17;*/
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-row {
/*border: 1px solid #000c17;*/
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-item-content {
/*border-right: 1px solid #000c17;*/
}
/* form 校验信息样式 */
.form-descriptions-word .ant-form-explain {
margin-left: 10px;
}
.form-descriptions-word {
text-align: left;
}
.form-descriptions-word .ant-descriptions-item-content {
position: relative;
}
.relative {
position: relative;
}
.form-descriptions-word .triangle-top-left {
position: absolute;
top: 1px;
left: 1px;
width: 0;
height: 0;
border-top: 10px solid red;
border-right: 10px solid transparent;
z-index: 9;
}
.relative .triangle-top-left {
position: absolute;
top: 2px;
left: 2px;
width: 0;
height: 0;
border-top: 10px solid red;
border-right: 10px solid transparent;
z-index: 9;
}
.form-descriptions-word .title {
width: 100%;
text-align: center;
font-size: 24px;
font-weight: bold;
margin-bottom: 10px;
}
/* 清除 a-input 默认样式 */
.form-descriptions-word .ant-input {
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .ant-input-group-addon {
border: 0;
background-color: #fff;
}
/* 清除 a-select 默认样式 */
.form-descriptions-word .ant-select-selection {
border: 0 !important;
box-shadow: none !important;
}
.form-descriptions-word .required-span-input {
margin-left: -20px;
float: left;
color: red;
padding-top: 10px;
}
.form-descriptions-word .required-span-textarea {
margin-left: -20px;
float: left;
color: red;
padding-top: 84px;
}
.form-descriptions-word .ant-descriptions-bordered.ant-descriptions-small .ant-descriptions-item-content {
padding: 0;
}
.form-descriptions-word .ant-form-item {
margin: 0 !important;
}
.form-descriptions-word .ant-input[disabled] {
background-color: #ffffff;
}
.form-descriptions-word .ant-input:hover, textarea.ant-input:hover {
border-color: white;
}
.form-descriptions-word .ant-select-selection {
border: none;
}
.form-descriptions-word textarea.ant-input {
height: auto;
margin-bottom: 0;
padding: 10px 11px;
}
.form-descriptions-word .ant-descriptions-item-label.ant-descriptions-item-colon {
width: 180px;
text-align: center;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-item-label {
background-color: #fff;
border-right: 1px solid #000c17;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-row {
border: 1px solid #000c17;
}
.form-descriptions-word .ant-descriptions-bordered .ant-descriptions-item-content {
border-right: 1px solid #000c17;
}
.form-descriptions-word .ant-form-explain {
float: left;
margin-left: 10px;
}
.form-descriptions-word .triangle-top-left {
position: absolute;
width: 0;
height: 0;
border-top: 10px solid red;
border-right: 10px solid transparent;
z-index: 9;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="6395" height="1079" viewBox="0 0 6395 1079">
<defs>
<clipPath id="clip-path">
<rect width="6395" height="1079" transform="translate(-5391)" fill="#fff"/>
</clipPath>
<linearGradient id="linear-gradient" x1="0.747" y1="0.222" x2="0.973" y2="0.807" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#1890ff"/>
<stop offset="1" stop-color="#2696ff"/>
</linearGradient>
</defs>
<g id="Mask_Group_1" data-name="Mask Group 1" transform="translate(5391)" clip-path="url(#clip-path)">
<g id="Group_118" data-name="Group 118" transform="translate(-419.333 -1.126)">
<path id="Path_142" data-name="Path 142" d="M6271.734-6.176s-222.478,187.809-55.349,583.254c44.957,106.375,81.514,205.964,84.521,277,8.164,192.764-156.046,268.564-156.046,268.564l-653.53-26.8L5475.065-21.625Z" transform="translate(-4876.383 0)" fill="#f1f5f8"/>
<path id="Union_6" data-name="Union 6" d="M-2631.1,1081.8v-1.6H-8230.9V.022H-2631.1V0H-1871.4s-187.845,197.448-91.626,488.844c49.167,148.9,96.309,256.289,104.683,362.118,7.979,100.852-57.98,201.711-168.644,254.286-65.858,31.29-144.552,42.382-223.028,42.383C-2441.2,1147.632-2631.1,1081.8-2631.1,1081.8Z" transform="translate(3259.524 0.803)" fill="url(#linear-gradient)"/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="a622e68e-7a65-46e9-94a9-d455de519afc" data-name="Layer 1" width="971.44" height="502" viewBox="0 0 971.44 502">
<defs>
<linearGradient id="341b0e5e-a21f-44db-b85f-76180f33f0d3" x1="599.5" y1="668.05" x2="599.5" y2="199" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="gray" stop-opacity="0.25"/>
<stop offset="0.54" stop-color="gray" stop-opacity="0.12"/>
<stop offset="1" stop-color="gray" stop-opacity="0.1"/>
</linearGradient>
<linearGradient id="9c19d1ba-0c1d-4cca-8c15-e6f3831a5e67" x1="485.72" y1="258.88" x2="485.72" y2="71.12" xlink:href="#341b0e5e-a21f-44db-b85f-76180f33f0d3"/>
<linearGradient id="fe76f7c7-2126-4e48-920d-21143a22d340" x1="132" y1="515" x2="303" y2="515" xlink:href="#341b0e5e-a21f-44db-b85f-76180f33f0d3"/>
<linearGradient id="2cf89a04-5a05-413b-983a-d2bc296cbb5e" x1="933" y1="568.28" x2="1031" y2="568.28" xlink:href="#341b0e5e-a21f-44db-b85f-76180f33f0d3"/>
</defs>
<title>responsive</title>
<g opacity="0.7">
<path d="M852.69,199H346.31A16.37,16.37,0,0,0,330,215.42V563.94a16.37,16.37,0,0,0,16.31,16.42H520.47v60.16h-7.94a8.3,8.3,0,0,0-8.27,8.33v12.07h16.21v7.14H678.53v-7.14h16.21V648.85a8.3,8.3,0,0,0-8.27-8.33H679V640h-.51V580.36H852.69A16.37,16.37,0,0,0,869,563.94V215.42A16.37,16.37,0,0,0,852.69,199Z" transform="translate(-114.28 -199)" fill="url(#341b0e5e-a21f-44db-b85f-76180f33f0d3)"/>
</g>
<rect x="407.72" y="371" width="156" height="92" fill="#bdbdbd"/>
<g opacity="0.1">
<path d="M525.07,579H675.24c1.81-7.87,3.26-13,3.26-13h-157S523.11,571.11,525.07,579Z" transform="translate(-114.28 -199)"/>
</g>
<path d="M235.82,3h499.8a16.1,16.1,0,0,1,16.1,16.1V327a0,0,0,0,1,0,0h-532a0,0,0,0,1,0,0V19.1A16.1,16.1,0,0,1,235.82,3Z" fill="#5e6368"/>
<path d="M849.9,576H350.1A16.1,16.1,0,0,1,334,559.9V526H866v33.9A16.1,16.1,0,0,1,849.9,576Z" transform="translate(-114.28 -199)" fill="#bdbdbd"/>
<circle cx="485.72" cy="352" r="9" fill="#5e6368"/>
<path d="M399.89,436H571.55a8.17,8.17,0,0,1,8.17,8.17V456a0,0,0,0,1,0,0h-188a0,0,0,0,1,0,0V444.17A8.17,8.17,0,0,1,399.89,436Z" fill="#bdbdbd"/>
<g opacity="0.5">
<rect x="320.72" y="71.12" width="330" height="187.76" rx="4.5" ry="4.5" fill="url(#9c19d1ba-0c1d-4cca-8c15-e6f3831a5e67)"/>
</g>
<rect x="324.95" y="72.5" width="321.54" height="183.96" rx="4.5" ry="4.5" fill="#fff"/>
<g opacity="0.5">
<rect x="414.52" y="98.91" width="35.44" height="31.9" rx="4.5" ry="4.5" fill="#0960bd"/>
</g>
<rect x="460.59" y="98.91" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
<rect x="460.59" y="109.55" width="79.54" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
<g opacity="0.5">
<rect x="414.52" y="148.53" width="35.44" height="31.9" rx="4.5" ry="4.5" fill="#0960bd"/>
</g>
<rect x="460.59" y="148.53" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
<rect x="460.59" y="159.16" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
<g opacity="0.5">
<rect x="414.52" y="198.15" width="35.44" height="31.9" rx="4.5" ry="4.5" fill="#0960bd"/>
</g>
<rect x="460.59" y="198.15" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
<rect x="460.59" y="208.78" width="96.33" height="3.54" rx="1.59" ry="1.59" fill="#e0e0e0"/>
<line x1="485.72" y1="42" x2="485.72" y2="20" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<line x1="485.72" y1="79" x2="485.72" y2="50.13" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<circle cx="485.72" cy="79" r="4" fill="#0960bd"/>
<circle cx="485.72" cy="46" r="4" fill="none" stroke="#fff" stroke-miterlimit="10"/>
<line x1="485.72" y1="42" x2="485.72" y2="20" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<line x1="485.72" y1="79" x2="485.72" y2="50.13" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<circle cx="485.72" cy="79" r="4" fill="#0960bd"/>
<circle cx="485.72" cy="46" r="4" fill="none" stroke="#fff" stroke-miterlimit="10"/>
<line x1="485.72" y1="279" x2="485.72" y2="310" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<line x1="485.72" y1="251" x2="485.72" y2="279.87" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<circle cx="485.72" cy="251" r="4" fill="#0960bd"/>
<line x1="305.72" y1="168.5" x2="274.22" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<line x1="333.22" y1="168.5" x2="304.35" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<circle cx="333.22" cy="168.5" r="4" fill="#0960bd"/>
<g opacity="0.1">
<rect x="408.22" y="435.5" width="156" height="3"/>
</g>
<g opacity="0.7">
<path d="M293.48,566.06H221.08l1-8.14c20.46-18.37,33.69-67.31,33.69-67.31a6.78,6.78,0,0,0-.87.18c-12,2.42-20.54,7.35-26.51,13.28l2.54-21.66c37.8-8.14,52.79-58.14,52.79-58.14-24.12,5.35-39.16,13.63-48.5,21.49l3.72-31.82c25.56,8.77,52-37.82,52-37.82l-1-.21.5-.32-.76.27c-28.25-6.09-43.35,10.06-48.25,16.77l.37-3.12q-1.12,3-2.18,5.88h0l0,.08q-3,8.13-5.49,16.06l0,0h0q-2.17,6.77-4.06,13.4l0-.06s-1.17-28.46-31.18-35.95c0,0,3.15,62.07,26.93,51.91h0c-2.2,9-4,17.66-5.56,26.07h0q-1.49,8.21-2.6,16l-.14.16.14-.12-.06.41v0h0q-1,7.07-1.7,13.78c.46-8.62-1.11-33.52-30.45-56.92,0,0-39,68.54,27.5,82,.15.13.3.26.44.38l-.1-.31.6.13.27-3.52a369.39,369.39,0,0,0,.23,44.1h0c.07,1,.14,2,.21,2.95H141.37c-27.94,57.79,15.52,89.46,15.52,89.46h120C323.49,596.66,293.48,566.06,293.48,566.06Zm-78-65.68h0v0Z" transform="translate(-114.28 -199)" fill="url(#fe76f7c7-2126-4e48-920d-21143a22d340)"/>
</g>
<path d="M217,588s-19-83,23-190" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="3" opacity="0.6"/>
<path d="M143,563H290s29,37-16,92H158S116,617,143,563Z" transform="translate(-114.28 -199)" fill="#3fa4ff"/>
<path d="M237.89,403.5s14.61-26,49.61-18c0,0-28.93,49.26-55,33.13Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
<path d="M228.63,431.09S227.5,404.5,198.5,397.5c0,0,3,58,26,48.5Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
<path d="M219.15,470.36s5.35-27.86,61.35-39.86c0,0-17.86,57.62-63.93,55.31Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
<path d="M214.61,501.63s5.89-29.13-29.11-56.13c0,0-38,64.67,27.48,76.83Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
<path d="M213.56,541.67S209.5,500.5,253.5,492.5c0,0-16.07,57.49-40,67.74Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
<path d="M233,419s38-29,54-34" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
<path d="M216.5,485.5s46-49,64-55" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
<path d="M198.5,397.5s28,38,26,48" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
<path d="M185.5,445.5s15,68,27,77" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
<path d="M213.5,560.5s24-66,40-68" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
<g opacity="0.1">
<path d="M290,563H143c-.33.67-.65,1.34-1,2H285s28.29,36.11-14.4,90H274C319,600,290,563,290,563Z" transform="translate(-114.28 -199)"/>
</g>
<rect y="455.6" width="971.44" height="32.93" fill="#e0e0e0"/>
<rect x="41.16" y="488.53" width="889.11" height="13.47" fill="#e0e0e0"/>
<rect x="41.16" y="488.53" width="889.11" height="4.49" opacity="0.1"/>
<line x1="690.22" y1="168.5" x2="696.22" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<line x1="637.22" y1="168.5" x2="682.1" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
<circle cx="637.22" cy="168.5" r="4" fill="#0960bd"/>
<circle cx="686.22" cy="168.5" r="4" fill="none" stroke="#fff" stroke-miterlimit="10"/>
<g opacity="0.7">
<path d="M1027,643.88l.1-.15q.31-.48.61-1l.11-.19q.29-.49.55-1l.09-.17c.2-.39.39-.78.56-1.19h0a23.79,23.79,0,0,0,.94-2.51l.1-.33c.09-.31.18-.62.26-.93l.1-.44q.1-.42.18-.85c0-.16.06-.32.09-.48s.09-.56.13-.85,0-.33.06-.49.06-.61.08-.92c0-.14,0-.29,0-.43,0-.45,0-.91,0-1.36V548h-13.85V507.52h-17V548H988.39V489.86h-17V548H965V481.55h-17V548H933V630.6c0,13.48,11.21,24.4,25,24.4H1006a25.19,25.19,0,0,0,20.24-10.06l0,0Q1026.61,644.41,1027,643.88Z" transform="translate(-114.28 -199)" fill="url(#2cf89a04-5a05-413b-983a-d2bc296cbb5e)"/>
</g>
<rect x="835.72" y="321" width="16" height="100" fill="#5e6368"/>
<rect x="835.72" y="288" width="16" height="33" fill="#3ad29f"/>
<rect x="857.72" y="329" width="16" height="100" fill="#5e6368"/>
<rect x="857.72" y="296" width="16" height="33" fill="#4d8af0"/>
<rect x="884.72" y="346" width="16" height="100" fill="#5e6368"/>
<rect x="884.72" y="313" width="16" height="33" fill="#f55f44"/>
<path d="M821.72,352h92a0,0,0,0,1,0,0v79.5a23.5,23.5,0,0,1-23.5,23.5h-45a23.5,23.5,0,0,1-23.5-23.5V352A0,0,0,0,1,821.72,352Z" fill="#3fa4ff"/>
<g opacity="0.1">
<path d="M936,551v4h88v79.5a23.39,23.39,0,0,1-5,14.49,23.45,23.45,0,0,0,9-18.49V551Z" transform="translate(-114.28 -199)"/>
</g>
</svg>
<template>
<div class="content">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'default-layout',
data() {
return {}
}
}
</script>
<style scoped>
.content {
width: 100%;
}
</style>
<template>
<div class="tabs">
<a-tabs @change="tabChange" type="card">
<slot></slot>
</a-tabs>
</div>
</template>
<script>
export default {
name: 'tabs-card',
data() {
return {}
},
methods: {
tabChange(key) {
this.$emit('tabChange', key)
}
}
}
</script>
<template>
<div class="page">
<div class="page-list" :style="{'width': leftWidth}">
<slot name="left"></slot>
</div>
<div class="page-list" :style="{'width': rightWidth}">
<slot name="right"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'todo-list-page',
props: {
leftWidth: {
type: String,
required: false,
default: () => {
return '49%'
}
},
rightWidth: {
type: String,
required: false,
default: () => {
return '49%'
}
}
},
data() {
return {}
}
}
</script>
<style lang="less" scoped>
.page {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
.page-list {
height: 100%;
}
}
</style>
<template>
<div class="list" :style="{margin: clearBottom ? '' : '12px 0'}">
<div class="list-header">
<div class="title">
<i></i>
<p class="style-title">
<slot name="title">标题</slot>
</p>
</div>
<div class="options">
<div class="search">
<slot name="search"/>
</div>
<slot name="options"/>
</div>
</div>
<div class="content" :style='{padding: clearPadding ? "0" : "10px 20px"}'>
<slot name="content"/>
</div>
</div>
</template>
<script>
export default {
name: 'todo-list',
props: {
clearBottom: {
type: Boolean,
required: false,
default: () => {
return false
}
},
clearPadding: {
type: Boolean,
required: false,
default: () => {
return false
}
}
},
data() {
return {}
}
}
</script>
<style lang="less" scoped>
.list {
width: 100%;
.list-header {
width: 100%;
height: 44px;
display: flex;
justify-content: space-between;
background-color: #e6f7ff;
.title {
height: 100%;
display: flex;
align-items: center;
i {
width: 4px;
height: 14px;
display: block;
margin: 13px 7px 13px 20px;
background-color: #1890ff;
}
.style-title {
height: 100%;
font-size: 15px;
font-weight: 600;
display: flex;
align-items: center;
margin-bottom: 0;
}
}
.options {
display: flex;
align-items: center;
.ant-btn {
margin-right: 15px;
}
.search {
height: 100%;
display: flex;
align-items: center;
margin-right: 15px;
}
i {
font-size: 20px;
margin-right: 20px;
}
}
}
.content {
width: 100%;
background-color: #fff;
}
}
</style>
import DefaultLayout from './Default-Layout'
import TodoList from './Todo-List'
import TabsCard from './Tabs-Card'
import TodoListPage from './Todo-List-Page'
import HrhxDate from '../HrhxDatePicker/HrhxDate'
import HrhxDatePicker from '../HrhxDatePicker/HrhxDatePicker'
import HrhxDateRangePicker from '../HrhxDatePicker/HrhxDateRangePicker'
const components = [
DefaultLayout,
TodoList,
TabsCard,
TodoListPage,
HrhxDate,
HrhxDatePicker,
HrhxDateRangePicker
]
const install = function(Vue) {
components.forEach(component => {
Vue.component(component.name, component)
})
}
if (typeof window !== 'undefined' && window.Vue) {
install(Window.Vue)
}
export default {
install,
DefaultLayout,
TodoList,
TabsCard,
TodoListPage,
HrhxDate,
HrhxDatePicker,
HrhxDateRangePicker
}
<template>
<hrhx-date-picker
:disabled="disabled || readOnly"
:placeholder="placeholder"
@change="handleDateChange"
:value="momVal"
:showTime="showTime"
:format="dateFormat"
:style="styleValue"
:getCalendarContainer="getCalendarContainer"
:disabledDate="disabledDate"
:disabledTime="disabledTime"
/>
</template>
<script>
import moment from 'moment'
export default {
name: 'HrhxDate',
props: {
placeholder: {
type: String,
default: '',
required: false
},
value: {
type: String,
required: false
},
dateFormat: {
type: String,
default: 'YYYY-MM-DD',
required: false
},
//此属性可以被废弃了
triggerChange: {
type: Boolean,
required: false,
default: false
},
readOnly: {
type: Boolean,
required: false,
default: false
},
disabled: {
type: Boolean,
required: false,
default: false
},
// showTime: {
// type: Boolean,
// required: false,
// default: false
// },
getCalendarContainer: {
type: Function,
default: () => document.body
},
disabledDate: {
type: Function,
default: () => {
return false
}
},
disabledTime: {
type: Function,
default: () => {
return false
}
},
styleValue: {
type: String,
required: false,
default: () => {
return 'width: 100%;'
}
}
},
computed: {
showTime() {
return this.dateFormat === 'YYYY-MM-DD HH:mm:ss'
}
},
data() {
let dateStr = this.value
return {
decorator: '',
momVal: !dateStr ? null : moment(dateStr, this.dateFormat)
}
},
watch: {
value(val) {
if (!val) {
this.momVal = null
} else {
this.momVal = moment(val, this.dateFormat)
}
}
},
methods: {
moment,
handleDateChange(mom, dateStr) {
this.$emit('change', dateStr)
}
},
//2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼
model: {
prop: 'value',
event: 'change'
}
}
</script>
<template>
<span>
<!--第一部分输入时间-->
<span class="ant-calendar-picker">
<div class="flex-items">
<input v-if="conversion"
class="ant-calendar-picker-input ant-input"
@keyup.enter="dataInputChang"
@click="_openChoiseModal"
@focus="_openChoiseOpenModal"
v-model="dateValue"
placeholder="请选择日期"
:disabled="disabled">
<!-- 转换按钮 -->
<a-icon v-if="conversion && !disabled" slot="addonAfter" class="ml5" type="sync"
@click="_conversionClick"/>
</div>
</span>
<!--第二部分选择时间-->
<a-date-picker
v-if="!conversion"
v-model="dateValue"
@change="changeSelectDate"
placeholder="请选择停机日期"
:format="dateFormat"
:disabledDate="disabledDate"
:disabledTime="disabledTime"
@panelChange="_openChoiseModal"
@openChange="_openChoiseOpenModal"
:disabled="disabled"
showTime
/>
</span>
</template>
<script>
import moment from 'moment'
export default {
name: 'HrhxDatePicker',
props: {
value: {
type: Object | String,
required: false
},
dateFormat: {
type: String,
required: false,
default: 'YYYY-MM-DD HH:mm:ss'
},
disabledDate: {
required: false,
default: null
},
disabledTime: {
required: false,
default: null
},
disabled: {
type: Boolean,
required: false,
default: false
},
type: {
type: String,
required: false,
default: null
}
},
data() {
return {
conversion: true,
dateValue: ''
}
},
watch: {
value: {
immediate: true,
handler(val) {
console.log(val)
if (val) {
this.dateValue = moment(val).format(this.dateFormat)
} else {
this.dateValue = null
this.conversion = true
this._openChoiseModal()
}
}
},
dateValue: {
immediate: true,
handler(val) {
if (!val) {
this._openChoiseModal()
}
}
}
},
methods: {
_conversionClick() {
this.conversion = !this.conversion
if (this.dateValue) {
this.dateValue = moment(this.dateValue, this.dateFormat)
this.$emit('change', this.dateValue, this.dateValue.format(this.dateFormat))
} else {
this.dateValue = moment(moment().locale('zh-cn'), this.dateFormat)
this.$emit('change', this.dateValue, this.dateValue.format(this.dateFormat))
}
},
_openChoiseOpenModal(status) {
this.$emit('openChange', status)
},
_openChoiseModal() {
console.log('----------------------------------进来了--------------------------------------')
this.$emit('panelChange', moment(moment().locale('zh-cn'), this.dateFormat), 'date')
this.$emit('panelChange', moment(moment().locale('zh-cn'), this.dateFormat), 'time')
},
changeSelectDate(val, dateString) {
this.$emit('change', val, dateString)
},
dataInputChang(e) {
if (this.$projectUtils.format(e.target.value)) {
this.dateValue = this.$projectUtils.format(e.target.value)
let result = moment(this.dateValue, this.dateFormat)
// 先触发change里的逻辑
this.$emit('change', result, moment(result).format(this.dateFormat))
if (this.type) {
this.$emit('chooseDate', this.type, this.dateValue)
}
// 验证逻辑合格进来 不合格走else 并清空数据
if (this.disabledDate == null || this._timeValid(result)) {
// this.$emit('change', result, moment(result).format(this.dateFormat))
} else {
this.dateValue = ''
this.$emit('change', null, '')
this.$message.warn('输入的时间不合理')
}
} else {
this.dateValue = ''
this.$message.warn('输入的时间格式不合法')
}
},
/**
* 校验时间
* @param datatime
* @returns {boolean}
* @private
*/
_timeValid(datatime) {
debugger
// 校验日期
if (this.disabledDate(datatime)) {
return false
}
// 校验时间--获取时间的小时数
if (this.disabledTime && this.disabledTime(datatime)) {
let disabledHours = this.disabledTime(datatime).disabledHours()
let hours = datatime.hours()
if (disabledHours.includes(hours)) {
return false
}
// 校验时间--获取时间的分钟数
let disabledMinutes = this.disabledTime(datatime).disabledMinutes()
console.log(disabledMinutes)
let minutes = datatime.minutes()
if (disabledMinutes.includes(minutes)) {
return false
}
}
return true
}
}
}
</script>
<template>
</template>
<script>
export default {
name: 'HrhxDateRangePicker'
}
</script>
<style scoped>
</style>
#### 1.\_util 包:存放自定义函数 详细见代码注释
#### 2.AvatarList:显示头像群并支持 tip,用法参考 src\views\Home.vue(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12181253_O0Xi.png '在这里输入图片标题')
#### 3.chart 包:存放各种图表相关的组件,条形图柱形图折线图等等 具体用法参考首页
#### 4.countDown 包:一个倒计时组件,用法参考 home 页,简单描述,该组件有 3 个属性,
target(时间/毫秒数)必填, format(function,该方法接收一个毫秒数的参数,用于格式化显示当前倒计时时间)非必填, onEnd 倒计时结束触发函数 ![输入图片说明](https://static.oschina.net/uploads/img/201904/12182046_mwqJ.png '在这里输入图片标题')
#### 5.dict 包:数据字典专用,用法参考文件夹下 readme 文件
#### 6.Ellipsis 包:字符串截取组件,可以指定字符串的显示长度,并将全部内容显示到 tip 中,简单使用参考 src\views\system\PermissionList.vue
#### 7.jeecg 包:该包下自定义了很多列表/表单中用到的组件 参考包下 readme 文件
#### 8.jeecgbiz 包:该包下定义了一些业务相关的组件,比如选择用户弹框,根据部门选择用户等等
#### 9.layouts+page 包:系统页面布局相关组件,比如登陆进去之后页面顶部显示什么,底部显示什么,菜单点击触发多个 tab 的布局等等 一般情况不需要修改
#### 10.menun 包:菜单组件,俩个,一个折叠菜单一个正常显示的菜单
#### 11.NumberInfo:数字信息显示组件 如下图
![输入图片说明](https://static.oschina.net/uploads/img/201904/12185858_uvJ5.png '在这里输入图片标题')
#### 12.online 包:该包下封装了 online 表单的相关组件,用于展示表单各种控件,验证表单等等,相关用法参考 readme
#### 13.setting 包:该包下封装了首页风格切换等功能如下图
![输入图片说明](https://static.oschina.net/uploads/img/201904/12190520_jySG.png '在这里输入图片标题')
#### 14.table 包:一个二次封装的 table 组件,用于展示列表,参考 readme
#### 15.tools 包:
Breadcrumb.vue:面包屑二次封装,支持路由跳转
DetailList.vue:详情展示用法参考 src\views\profile\advanced\Advanced.vue(效果如下图) ![输入图片说明](https://static.oschina.net/uploads/img/201904/12193954_Uar6.png '在这里输入图片标题')
```
个人认为该页面代码有两点值得学习:
1.vue provide/inject的使用
2.该页面css定义方式,只定义一个顶层class,其余样式都定义在其下,这样只要顶层class不和别的页面冲突,整个页面的样式都是唯一生效的
```
FooterToolBar.vue:fixed 定位的底部,通过是否定义内部控件的属性 slot="extra"决定是左浮动或是右浮动 HeaderNotice.vue:首页通知(如下图) ![输入图片说明](https://static.oschina.net/uploads/img/201904/12195340_fPe0.png '在这里输入图片标题')
HeaderInfo.vue:上下文字布局(如下图) ![输入图片说明](https://static.oschina.net/uploads/img/201904/12195638_dG5o.png '在这里输入图片标题')
Logo.vue:首页左上侧的 log 图 ![输入图片说明](https://static.oschina.net/uploads/img/201904/12200908_ihv3.png '在这里输入图片标题')
UserMenu.vue:首页右上侧的内容 ![输入图片说明](https://static.oschina.net/uploads/img/201904/12201226_laQK.png '在这里输入图片标题')
#### 16.trend 包 趋势显示组件(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12201600_Wo8K.png '在这里输入图片标题') ![corn表达式](https://oscimg.oschina.net/oscnet/661f9ac09016395f9f49286143af3241623.jpg) ![corn控件添加清除按钮](https://oscimg.oschina.net/oscnet/15096e49f2e29bd829e304d56770025d03c.jpg)
<template>
<a-radio-group
v-if="tagType == 'radio'"
@change="handleInput"
:value="value ? value : dateValue"
:disabled="disabled"
>
<a-radio v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-radio>
</a-radio-group>
<a-select
v-else-if="tagType == 'select'"
:showSearch="showSearch"
allowClear
:placeholder="placeholder"
:disabled="disabled"
:value="value ? value : dateValue"
@change="handleInput"
>
<a-select-option
v-for="(item, key) in dictOptions"
:key="key"
:value="item.value"
:disabled="disabledData.indexOf(item.value) !== -1"
>
<span style="display: inline-block; width: 100%" :title="item.text || item.label">
{{ item.text || item.label }}
</span>
</a-select-option>
</a-select>
</template>
<script>
import { ajaxGetDictItems } from '@/api/services/base'
export default {
name: 'JDictSelectTag',
props: {
dictCode: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
triggerChange: Boolean,
disabled: Boolean,
showSearch: {
type: Boolean,
required: false,
default: () => {
return false
}
},
value: {
type: String,
default: ''
},
type: {
type: String,
default: ''
},
options: {
type: Array,
default: () => []
}
},
data() {
return {
dictOptions: [],
disabledData: ['email', 'shortMessage', 'wechatMessage'],
tagType: '',
dateValue: undefined
}
},
watch: {
dictCode: {
immediate: true,
handler() {
if (this.dictCode) {
this.initDictData()
}
}
},
options: {
immediate: true,
handler() {
if (this.options && this.options.length > 0) {
this.dateValue = undefined
this.setCurrentDictOptions(this.options)
}
}
}
},
created() {
if (!this.type || this.type === 'list') {
this.tagType = 'select'
} else {
this.tagType = this.type
}
},
methods: {
initDictData() {
//根据字典Code, 初始化字典数组
ajaxGetDictItems(this.dictCode, null).then(res => {
if (String(res.code) === '200') this.dictOptions = res.value
})
},
handleInput(e) {
let val
if (this.tagType == 'radio') {
val = e.target.value
} else {
val = e
}
if (this.triggerChange) {
this.dateValue = val
this.$emit('change', val)
} else {
this.$emit('input', val)
}
},
setCurrentDictOptions(dictOptions) {
this.dictOptions = dictOptions
}
}
}
</script>
/**
* 字典 util
* author: scott
* date: 20190109
*/
import { ajaxGetDictItems } from '@/api/services/base'
import { getAction } from '@/api/manage'
/**
* 获取字典数组
* @param dictCode 字典Code
* @return List<Map>
*/
export async function initDictOptions(dictCode) {
if (!dictCode) {
return '字典Code不能为空!'
}
//获取字典数组
let res = await ajaxGetDictItems(dictCode)
return res
}
/**
* 字典值替换文本通用方法
* @param dictOptions 字典数组
* @param text 字典值
* @return String
*/
export function filterDictText(dictOptions, text) {
//--update-begin----author:sunjianlei---date:20191025------for:修复字典替换方法在字典没有加载完成之前报错的问题、修复没有找到字典时返回空值的问题---
if (dictOptions instanceof Array) {
for (let dictItem of dictOptions) {
if (text === dictItem.value) {
return dictItem.text
}
}
}
return text
//--update-end----author:sunjianlei---date:20191025------for:修复字典替换方法在字典没有加载完成之前报错的问题、修复没有找到字典时返回空值的问题---
}
/**
* 字典值替换文本通用方法(多选)
* @param dictOptions 字典数组
* @param text 字典值
* @return String
*/
export function filterMultiDictText(dictOptions, text) {
if (!text || !dictOptions || dictOptions.length == 0) {
return ''
}
let re = ''
let arr = text.split(',')
dictOptions.forEach(function (option) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === option.value) {
re += option.text + ','
break
}
}
})
if (re == '') {
return text
}
return re.substring(0, re.length - 1)
}
/**
* 翻译字段值对应的文本
* @param children
* @returns string
*/
export async function ajaxFilterDictText(dictCode, key) {
if (!dictCode) {
return '字典Code不能为空!'
}
if (!key) {
return ''
}
//通过请求读取字典文本
let res = await getAction(`/sys/dict/getDictText/${dictCode}/${key}`)
if (res.success) {
return res.result
} else {
return ''
}
}
# JDictSelectTag 组件用法
---
- 从字典表获取数据,dictCode 格式说明: 字典 code
```html
<j-dict-select-tag v-model="queryParams.sex" placeholder="请输入用户性别" dictCode="sex" />
```
v-decorator 用法:
```html
<j-dict-select-tag v-decorator="['sex', {}]" :triggerChange="true" placeholder="请输入用户性别" dictCode="sex" />
```
- 从数据库表获取字典数据,dictCode 格式说明: 表名,文本字段,取值字段
```html
<j-dict-select-tag v-model="queryParams.username" placeholder="请选择用户名称" dictCode="sys_user,realname,id" />
```
# JDictSelectUtil.js 列表字典函数用法
---
- 第一步: 引入依赖方法
```js
import { initDictOptions, filterDictText } from '@/components/dict/JDictSelectUtil'
```
- 第二步: 在 created()初始化方法执行字典配置方法
```js
// 初始化字典配置
this.initDictConfig()
```
- 第三步: 实现 initDictConfig 方法,加载列表所需要的字典(列表上有多个字典项,就执行多次 initDictOptions 方法)
```js
function initDictConfig() {
//初始化字典 - 性别
initDictOptions('sex').then(res => {
if (res.success) {
this.sexDictOptions = res.result
}
})
}
```
- 第四步: 实现字段的 customRender 方法
```js
customRender: (text, record, index) => {
//字典值替换通用方法
return filterDictText(this.sexDictOptions, text)
}
```
import T from './JDictSelectTag.vue'
const JDictSelectTag = {
install: function (Vue) {
Vue.component('JDictSelectTag', T)
}
}
export default JDictSelectTag
import { getAction, postAction } from '@/api/manage'
export const queryTreeListBySysDepart = data => postAction('/sys/sysDepart/queryTreeListBySysDepart', data)
export const queryUserByDepId = params => getAction('/sys/user/queryUserByDepId', params)
export const queryUserListByUserIds = params => getAction('/sys/user/userListByUserIds', params)
export const selectUserlistByDeptIdAndParam = data => postAction('/sys/user/selectUserlistByDeptIdAndParam', data)
// 流程跟踪
export const processTrack = data => postAction('/activiti/histoicFlow', data)
// 流程图
export const processImage = params => getAction('/workflow/getProcessImageBase64', params)
// 获取任务节点
export const approvalGetFirstUserTask = params => getAction('/ticket/task/getFristUserTask', params)
export const getUserBusinessTaskByCurrentTaskId = params =>
getAction('/activiti/getUserBusinessTaskByCurrentTaskId', params)
// 撤销
export const getTaskCancelUrl = params => getAction('/activiti/getTaskCancelUrl', params)
// 催办
export const approvalTicketTaskUrge = data => postAction('/ticket/task/taskUrge', data)
// 获取流程配置
export const selectProjectPurchaseMainJsonConfig = params =>
getAction('/project/projectPurchaseMain/selectProjectPurchaseMainJsonConfig', params)
\ No newline at end of file
<template>
<div>
<!-- <admin-todo-list title="审批详情" icon="table"> -->
<admin-icon-title :read-only="true">
<template slot="title"> 审批详情 </template>
</admin-icon-title>
<a-table
bordered
size="small"
:columns="columns"
:dataSource="dataSource"
:pagination="false"
:scroll="{ x: 1000 }"
>
<span slot="electronicSignature" slot-scope="text, record">
<img
v-if="record.activityName != '申请单位'"
style="width: 150px; height: 50px; margin: 0 auto"
:src="imgPrefix + record.electronicSignature"
/>
<span v-else>{{ record.assigneeName }}</span>
</span>
</a-table>
<!-- </admin-todo-list> -->
</div>
</template>
<script>
import { postAction } from '@/api/manage.js'
import { mapGetters } from 'vuex'
export default {
props: {
actPojo: {
type: Object,
required: false,
default: () => {}
}
},
data() {
return {
columns: [
{
title: '审批环节',
align: 'center',
ellipsis: true,
width: '200px',
dataIndex: 'activityName'
},
{
title: '审批人',
align: 'center',
ellipsis: true,
width: '200px',
scopedSlots: {
filterDropdown: 'filterSetting',
filterIcon: 'filterSettingIcon',
customRender: 'electronicSignature'
}
},
{
title: '审批时间',
align: 'center',
ellipsis: true,
width: '180px',
dataIndex: 'endTime'
},
{
title: '审批意见',
align: 'center',
ellipsis: true,
dataIndex: 'comment'
}
],
dataSource: [],
interface: {
histoicFlow: ''
},
imgPrefix: window._CONFIG['resourcesUrl']
}
},
computed: {
...mapGetters(['useApprovalProcessInfo'])
},
watch: {
actPojo: {
immediate: true,
deep: true,
handler(val) {
if (val) {
this.getInit()
}
}
}
},
// 生命周期 - 创建完成(访问当前this实例)
created() {},
// 生命周期 - 挂载完成(访问DOM元素)
mounted() {},
// 函数方法 - 点击等操作执行的方法
methods: {
getInit() {
const actPojo = this.useApprovalProcessInfo.actPojo
if (!actPojo) return
let params = {
procInsId: actPojo.procInsId,
procDefName: actPojo.procDefName,
relationId: actPojo.id
}
postAction(this.interface.histoicFlow, params).then(res => {
let result = res?.value
if (!result) return
this.dataSource = result.filter(
item => item.activityName !== '开始' && item.activityName !== '结束' && item.endTime
)
})
}
}
}
</script>
<style scoped>
/* @import url(); 引入css类 */
</style>
\ No newline at end of file
<template>
<admin-modal
title="退回"
width="600px"
:visible.sync="visible"
:loading="loading"
@on-cancel="handleCancel"
@on-confirm="handleSubmit"
>
<a-form-model ref="ruleForm" :model="form" :rules="rules" class="form-descriptions-word">
<a-descriptions bordered size="small" class="descriptions-width">
<a-descriptions-item label="跳转环节" :span="3">
<AdminRequiredSign required />
<a-form-model-item prop="targetTaskDefKey">
<a-select v-model="form.targetTaskDefKey" placeholder="请选择跳转节点" style="width: 100%">
<a-select-option v-for="node in goBackNodes" :key="node.key" :value="node.key">
{{ node.name }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-descriptions-item>
<a-descriptions-item label="退回意见" :span="3">
<AdminRequiredSign required />
<a-form-model-item prop="approvalComment">
<a-textarea
v-model="form.approvalComment"
placeholder="请输入退回意见"
:auto-size="{ minRows: 3, maxRows: 5 }"
/>
</a-form-model-item>
</a-descriptions-item>
</a-descriptions>
</a-form-model>
</admin-modal>
</template>
<script>
import { mapGetters } from 'vuex'
import { throttle } from 'lodash'
export default {
name: 'ApprovalGoBack',
props: {
loading: {
type: Boolean,
default: false
}
},
data() {
return {
visible: false,
goBackNodes: [],
form: {
targetTaskDefKey: undefined,
approvalComment: ''
},
rules: {
targetTaskDefKey: [{ required: true, message: '请选择跳转环节!', trigger: 'change' }],
approvalComment: [{ required: true, message: '请输入审批意见!', trigger: 'blur' }]
}
}
},
computed: {
...mapGetters(['useApprovalProcessInfo'])
},
methods: {
/**
* 打开 Modal,初始化节点信息
* @author Matrix
* @since 2022/09/16
*/
openModal(dataSource) {
if (dataSource?.length) {
this.goBackNodes = dataSource
} else {
this.$message.warning('暂无可退回流程!')
return
}
this.visible = true
},
/**
* 取消
* @author Matrix
* @since 2022/090/13
*/
handleCancel() {
this.$refs.ruleForm.resetFields()
this.visible = false
},
/**
* 退回环节确认
* @author Matrix
* @since 2022/090/13
*/
handleSubmit: throttle(
function () {
const form = {
...this.useApprovalProcessInfo.actPojo,
...this.form
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.$emit('on-ok', form)
}
})
},
3000,
{ trailing: false }
)
}
}
</script>
<style lang="scss" scoped>
@import '~@/assets/css/descriptios-reset.css';
::v-deep .descriptions-width {
.ant-descriptions-item-label.ant-descriptions-item-colon {
width: 100px;
}
}
</style>
<template>
<admin-modal
width="40%"
:title="type"
:visible.sync="voidVisible"
:loading="voidLoading"
@on-cancel="handleVoidCancel"
@on-confirm="handleVoidConfirm"
>
<a-form-model ref="ruleForm" :model="form" :rules="rules">
<a-form-model-item v-if="type === '作废原因'" prop="workDeleteReason">
<a-textarea
v-model="form.workDeleteReason"
:auto-size="{ minRows: 2, maxRows: 6 }"
:placeholder="`请输入${type}`"
/>
</a-form-model-item>
<a-form-model-item v-else-if="type === '撤销原因'" prop="workCancelReason">
<a-textarea
v-model="form.workCancelReason"
:auto-size="{ minRows: 2, maxRows: 6 }"
:placeholder="`请输入${type}`"
/>
</a-form-model-item>
<a-form-model-item v-else prop="workRejectReason">
<a-textarea
v-model="form.workRejectReason"
:auto-size="{ minRows: 2, maxRows: 6 }"
:placeholder="`请输入${type}`"
/>
</a-form-model-item>
</a-form-model>
</admin-modal>
</template>
<script>
export default {
props: {
type: {
type: String,
default: ''
}
},
data() {
return {
voidLoading: false,
voidVisible: false,
form: {},
rules: {
workDeleteReason: [{ required: true, message: `请输入${this.type}`, trigger: 'blur' }],
workCancelReason: [{ required: true, message: `请输入${this.type}`, trigger: 'blur' }],
workRejectReason: [{ required: true, message: `请输入${this.type}`, trigger: 'blur' }]
}
}
},
// 生命周期 - 创建完成(访问当前this实例)
created() {},
// 生命周期 - 挂载完成(访问DOM元素)
mounted() {},
// 函数方法 - 点击等操作执行的方法
methods: {
openModal(id) {
this.voidVisible = true
this.$set(this.form, 'id', id)
switch (this.type) {
case '作废原因':
this.$set(this.form, 'workDeleteReason', '')
break
case '撤销原因':
this.$set(this.form, 'workCancelReason', '')
break
case '拒绝原因':
this.$set(this.form, 'workRejectReason', '')
break
}
},
handleVoidCancel() {
this.$nextTick(() => {
this.$refs.ruleForm.resetFields()
})
this.voidVisible = false
},
handleVoidConfirm() {
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.$emit('confirm', this.form)
this.voidVisible = false
}
})
}
}
}
</script>
<style scoped lang="scss">
/* @import url(); 引入css类 */
::v-deep .ant-form-item {
margin-bottom: 0 !important;
}
</style>
<template>
<div>
<a-row :gutter="12">
<a-col :span="8">
<h3>流程进度</h3>
<div class="process-scroll">
<a-steps :current="dataSource.length - 1" direction="vertical">
<a-step
v-for="(item, index) in dataSource"
:key="index"
:status="item.approvalComment && item.approvalComment.indexOf('[退回]') !== -1 ? 'error' : 'finish'"
:title="item.histIns.activityName"
:description="item.assigneeName"
size="small"
>
<a-icon
v-if="index === dataSource.length - 1 && item.histIns.activityType !== 'endEvent'"
slot="icon"
type="loading"
/>
</a-step>
</a-steps>
</div>
</a-col>
<a-col class="gutter-row" :span="16">
<h3>流程详情</h3>
<div class="process-scroll">
<a-table
bordered
size="small"
style="margin: 20px 0"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="false"
:scroll="{ x: 1000 }"
/>
</div>
</a-col>
</a-row>
<h3>流程图</h3>
<div id="processImage" class="process-image">
<!-- <img :src="processImageBase64" alt="" />-->
<iframe id="processIframe" :src="processIframeUrl" frameborder="0" width="100%" scrolling="auto"></iframe>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { processTrack } from '@/components/hrhx/approval/api/services/approve.js'
import { guid } from '@/utils/tools.js'
export default {
name: 'ApprovalProcessTrack',
data() {
return {
processImageBase64: '',
processIframeUrl: '',
columns: [
{
title: '执行环节',
align: 'center',
ellipsis: true,
width: '300px',
dataIndex: 'histIns.activityName',
customRender: t => this.$m.isEffectiveCommon(t)
},
{
title: '执行人',
align: 'center',
ellipsis: true,
width: '80px',
dataIndex: 'assigneeName',
customRender: t => this.$m.isEffectiveCommon(t)
},
{
title: '开始时间',
align: 'center',
ellipsis: true,
width: '180px',
dataIndex: 'histIns.startTime',
customRender: t => this.$m.isEffectiveCommon(t)
},
{
title: '结束时间',
align: 'center',
ellipsis: true,
width: '180px',
dataIndex: 'histIns.endTime',
customRender: t => this.$m.isEffectiveCommon(t)
},
{
title: '提交意见',
align: 'center',
ellipsis: true,
dataIndex: 'approvalComment',
customRender: t => this.$m.isEffectiveCommon(t)
},
{
title: '任务历时',
align: 'center',
ellipsis: true,
dataIndex: 'durationTime',
customRender: t => this.$m.isEffectiveCommon(t)
}
],
dataSource: []
}
},
computed: {
...mapGetters(['useApprovalProcessInfo'])
},
mounted() {
const iframeDom = document.getElementById('processIframe')
iframeDom.height = 400
// if (iframeDom?.attachEvent) {
// iframeDom.attachEvent('onload', function () {
// iframeDom.height = iframeDom.contentDocument.body.scrollHeight
// })
// } else {
// iframeDom.onload = function () {
// iframeDom.height = iframeDom.contentDocument.body.scrollHeight
// }
// }
},
methods: {
/**
* 初始化数据
* @author Matrix
* @since 2022/09/16
*/
getInit() {
this.getProcessTrack()
},
/**
* 获取流程审批步骤
* @author Matrix
* @since 2022/09/16
*/
getProcessTrack() {
const procInsId = this.useApprovalProcessInfo.actPojo.procInsId
processTrack(`procInsId=${procInsId}`).then(res => {
this.dataSource = res.value.map(item => {
item.id = guid()
return item
})
this.getProcessIframe()
})
},
/**
* 获取流程图的IFrame
* @author Matrix
* @since 2022/09/16
*/
getProcessIframe() {
const record = this.useApprovalProcessInfo.actPojo
let procDefId = ''
let procInsId = ''
if (!record.procDef || !record.procIns) {
procDefId = this.dataSource[this.dataSource.length - 1].procDefId
procInsId = record.procInsId
} else {
procDefId = record.procDef.id
procInsId = record.procIns.processInstanceId
}
const isProd = process.env.NODE_ENV
if (isProd !== 'production') {
this.processIframeUrl =
window._CONFIG['domainURL'] +
// process.env.VUE_APP_PROXY +
'//diagram-viewer/indexImage.html?processDefinitionId=' +
procDefId +
'&processInstanceId=' +
procInsId +
'&rnd=' +
new Date().getTime()
} else {
this.processIframeUrl =
window._CONFIG['domainURL'] +
// process.env.VUE_APP_API +
'//diagram-viewer/indexImage.html?processDefinitionId=' +
procDefId +
'&processInstanceId=' +
procInsId +
'&rnd=' +
new Date().getTime()
}
// setTimeout(() => {
// const iframeDom = document.getElementById('processIframe')
// iframeDom.height = iframeDom.contentDocument.body.scrollHeight
// }, 300)
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .process-image {
padding: 30px;
border-radius: 20px;
background-color: #eeeeee;
::v-deep .diagram {
border: 0 !important;
margin: 0 !important;
padding: 0 !important;
background: transparent;
}
img {
width: 100%;
}
::v-deep .wrapper {
::v-deep .diagram {
border: 0;
}
}
}
.process-scroll {
max-height: 280px;
overflow-x: hidden;
overflow-y: scroll;
}
/* 使用伪类选择器 ::-webkit-scrollbar ,兼容chrome和safari浏览器 */
.process-scroll::-webkit-scrollbar {
width: 0 !important; /* remove scrollbar space */
background: transparent; /* optional: just make scrollbar invisible */
}
/* 兼容IE10+ */
.process-scroll {
-ms-overflow-style: none;
}
/* 但是firefox代码无效 */
//.process-scroll {
// overflow: -moz-scrollbars-none;
//}
.process-scroll::-webkit-scrollbar {
width: 9px;
height: 9px;
}
.process-scroll::-webkit-scrollbar-track {
background: rgb(239, 239, 239);
border-radius: 2px;
}
.process-scroll::-webkit-scrollbar-thumb {
background: #bfbfbf;
border-radius: 10px;
}
.process-scroll::-webkit-scrollbar-thumb:hover {
background: #888;
}
.process-scroll::-webkit-scrollbar-corner {
background: #179a16;
}
</style>
<template>
<admin-modal title="进度跟踪" :visible.sync="visible" :loading="loading" :footer="null" @on-cancel="handleCancel">
<ApprovalProcessTrack ref="approvalProcessTrackRef" />
</admin-modal>
</template>
<script>
import ApprovalProcessTrack from './ApprovalProcessTrack'
import {
RESET_APPROVAL_PROCESS_INFO_AND_RULES,
SET_APPROVAL_PROCESS_INFO
} from '@/components/hrhx/approval/utils/set-process-rules.js'
export default {
name: 'ApprovalProcessTrackModal',
components: { ApprovalProcessTrack },
data() {
return {
visible: false,
loading: false
}
},
methods: {
async openModal(record) {
this.visible = true
const id = record.businessId
if (record.procDefCategory === '物资计划') {
SET_APPROVAL_PROCESS_INFO(data.value)
}
this.$nextTick(() => {
this.$refs.approvalProcessTrackRef.getInit()
})
},
handleCancel() {
RESET_APPROVAL_PROCESS_INFO_AND_RULES()
this.visible = false
}
}
}
</script>
<template>
<admin-modal
title="作废"
width="600px"
:visible.sync="visible"
:loading="loading"
@on-cancel="handleCancel"
@on-confirm="handleSubmit"
>
<a-form-model ref="ruleForm" :model="form" :rules="rules" class="form-descriptions-word">
<a-descriptions bordered size="small" class="descriptions-width">
<a-descriptions-item label="作废意见" :span="3">
<AdminRequiredSign required />
<a-form-model-item prop="approvalComment">
<a-textarea
v-model="form.approvalComment"
placeholder="请输入作废意见"
:auto-size="{ minRows: 3, maxRows: 5 }"
/>
</a-form-model-item>
</a-descriptions-item>
</a-descriptions>
</a-form-model>
</admin-modal>
</template>
<script>
import { mapGetters } from 'vuex'
import { throttle } from 'lodash'
export default {
name: 'ApprovalRefuse',
props: {
loading: {
type: Boolean,
default: false
}
},
data() {
return {
visible: false,
form: {
approvalComment: ''
},
rules: {
approvalComment: [{ required: true, message: '请输入审批意见!', trigger: 'blur' }]
}
}
},
computed: {
...mapGetters(['useApprovalProcessInfo'])
},
methods: {
openModal() {
this.visible = true
},
/**
* 取消
* @author Matrix
* @since 2022/090/13
*/
handleCancel() {
this.$refs.ruleForm.resetFields()
this.visible = false
},
/**
* 退回环节确认
* @author Matrix
* @since 2022/090/13
*/
handleSubmit: throttle(
function () {
const form = {
...this.useApprovalProcessInfo.actPojo,
...this.form
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.$emit('on-ok', form)
}
})
},
3000,
{ trailing: false }
)
}
}
</script>
<style lang="scss" scoped>
@import '~@/assets/css/descriptios-reset.css';
::v-deep .descriptions-width {
.ant-descriptions-item-label.ant-descriptions-item-colon {
width: 100px;
}
}
</style>
<template>
<admin-modal
title="催办"
width="600px"
:visible.sync="visible"
:loading="loading"
@on-cancel="handleCancel"
@on-confirm="handleSubmit"
>
<a-form-model ref="ruleForm" :model="form" :rules="rules" class="form-descriptions-word">
<a-descriptions bordered size="small" class="descriptions-width">
<a-descriptions-item label="催办方式" :span="3">
<AdminRequiredSign required />
<a-form-model-item prop="urgeTypeList">
<HDictCheckbox v-model="form.urgeTypeList" dict-code="urge_type" />
</a-form-model-item>
</a-descriptions-item>
</a-descriptions>
</a-form-model>
</admin-modal>
</template>
<script>
import { approvalTicketTaskUrge } from '@/components/hrhx/approval/api/services/approve.js'
import HDictCheckbox from '@/components/hrhx/form/HDictCheckbox'
export default {
name: 'ApprovalUrgeModal',
components: { HDictCheckbox },
data() {
return {
visible: false,
loading: false,
form: {
urgeTypeList: ''
},
urgeOptions: [],
prevParams: {},
rules: {
urgeTypeList: [{ required: true, message: '请选择催办方式!', trigger: 'change' }]
}
}
},
methods: {
openModal(record) {
this.visible = true
this.prevParams = record
},
handleCancel() {
this.$refs.ruleForm.resetFields()
this.visible = false
},
handleSubmit() {
this.$refs.ruleForm.validate(valid => {
if (valid) {
const params = {
...this.prevParams,
...this.form
}
this.loading = true
approvalTicketTaskUrge(params)
.then(() => {
this.$message.success('操作成功!')
this.handleCancel()
this.$emit('on-ok')
})
.finally(() => (this.loading = false))
}
})
}
}
}
</script>
<style lang="scss" scoped>
@import '~@/assets/css/descriptios-reset.css';
::v-deep .descriptions-width {
.ant-descriptions-item-label.ant-descriptions-item-colon {
width: 100px;
}
}
</style>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
import store from '@/store'
const PROPOSED_VOTE_LINK = 'trunk_user_task_register' // 设定拟票环节标识
/**
* 前提是将此函数的this指向当前调用的页面
* params ruleName 当前页的规则名
* @returns rules 需要验证的新规则
*/
export function setFormRules(ruleName) {
const TICKET_INFO = store.getters['useTicketInfo'] // 当前票的详细信息,由对票据进行操作的时候传入 vuex
const ALL_LINKS = store.getters['useTicketAllLinks'] // 当前票的所有环节,由对票据进行操作的时候传入 vuex
const CURRENT_LINK = TICKET_INFO?.actPojo?.taskDefKey // 当前票的当前环节,由对票据进行操作的时候传入 vuex
let LINK_CODE = ''
let NEW_RULES = {}
if (ALL_LINKS && ALL_LINKS.length) {
!!CURRENT_LINK && CURRENT_LINK !== '拟票' ? (LINK_CODE = CURRENT_LINK) : (LINK_CODE = PROPOSED_VOTE_LINK)
const current_link_info = ALL_LINKS.find(link => link.linkCode === LINK_CODE)
if (current_link_info) {
if (current_link_info?.defaultStatus) {
if (current_link_info?.fields?.length) {
current_link_info.fields.forEach(field => {
if (this?.[ruleName][field.fieldName]) {
this.$delete(this[ruleName], field.fieldName)
}
})
}
NEW_RULES = this[ruleName]
} else {
if (current_link_info?.fields?.length) {
current_link_info.fields.forEach(field => {
if (field?.switch) {
if (this?.[ruleName][field.fieldName]) {
NEW_RULES[field.fieldName] = this[ruleName][field.fieldName]
}
}
})
}
}
}
}
this[ruleName] = NEW_RULES
// if (this.$refs[formRef]) {
// this.$refs[formRef].setRules(this[ruleName])
// }
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论