Commit 6aa710ea by 温丽香

init project

0 parents
Showing with 3774 additions and 0 deletions
src/client/*
\ No newline at end of file
module.exports = {
root: true,
parserOptions: {
parser: '@typescript-eslint/parser',
},
env: {
browser: true,
},
'extends': [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'@vue/standard',
'@vue/typescript',
],
// required to lint *.vue files
plugins: [
'vue',
],
// add your custom rules here
rules: {
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'max-len': [2, {
code: 300,
ignoreStrings: true,
ignoreTemplateLiterals: true,
ignoreComments: true,
ignoreRegExpLiterals: true,
ignoreUrls: true,
ignoreTrailingComments: true,
}],
'no-var': 2, // 不接受 var 声明变量
'prefer-const': 2, // const 优先
'one-var': [2, 'never'], // 不允许使用','连续声明变量
'object-curly-spacing': [2, 'always'], // 大括号内必须有空格
'eqeqeq': [2, 'smart'], // 尽量使用全等
'camelcase': [0, { properties: 'never' }], // 强制使用驼峰变量名,不检测大写变量名,对象中属性不做检测
'arrow-parens': [2, 'as-needed'], // 箭头函数需使用括号 (一个参数时可省略)
'no-useless-escape': 2, // 禁止不必要的 call/apply
'no-unneeded-ternary': 2, // 禁止不必要的嵌套
'no-nested-ternary': 2, // 禁用嵌套的三目运算符
'no-const-assign': 2, // 禁止修改 const 变量
'spaced-comment': [2, 'always'], // 注释需要有空格
'array-bracket-spacing': [2, 'never'], // 数组方括号内不可有空格 eg: [1, 3]可以; [1, 3 ]不可以
// 'dot-notation': 2, // 禁用不必要的方括号 eg: foo[bar] 如果bar不是变量,请用 foo.bar
'prefer-spread': 2, // 优先使用 展开运算 ...arg
'template-curly-spacing': [2, 'never'], // 模版字符串中不可以有空格 eg: `${ people.name }` 空格需要去掉
'comma-dangle': ['off'], // 当最后一个元素或属性与闭括号 ] 或 } 在 不同的行时,要求使用拖尾逗号
'no-eval': 2, // 不许使用 eval 函数
'no-duplicate-imports': 2, // 同一个文件中的方法引用,需要在一次写完
'no-fallthrough': 2, // switch 一定 要用break 中断
'no-unreachable': 2, // return throw continue break 后不可跟随 无法运行到的代码
'use-isnan': 2, // NaN只可使用 isNaN 函数判断
'space-before-function-paren': [2, 'never'], // 方法与括号间不允许出现空格
'generator-star-spacing': [2, 'both'], // generator函数 *号 两边需要空格
'import/no-webpack-loader-syntax': 'off',
},
globals: {
'_': true,
'Vue': true,
'moment': true,
'Toast': true,
'vm': true,
'echarts': true,
},
}
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# aiot
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
"env": {
"development": {
"plugins": ["dynamic-import-node"]
}
}
}
This diff could not be displayed because it is too large.
{
"name": "aiot",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"api": "npx openapi-generator generate -i http://218.94.122.141:8089/swagger/doc.json -g typescript-axios -o ./src/client --skip-validate-spec"
},
"dependencies": {
"@json-editor/json-editor": "^1.4.0-beta.0",
"ace-builds": "^1.4.12",
"axios": "^0.18.0",
"bin-code-editor": "^0.9.0",
"core-js": "^3.6.5",
"crypto-js": "^3.1.9-1",
"dompurify": "^2.0.7",
"element-ui": "^2.15.3",
"exif-js": "^2.3.0",
"jquery": "^3.3.1",
"lodash": "^4.17.20",
"moment": "^2.24.0",
"nprogress": "^0.2.0",
"popper.js": "^1.14.3",
"pug": "^3.0.2",
"pug-plain-loader": "^1.1.0",
"qrcodejs2": "0.0.2",
"svg.js": "^2.7.1",
"tippy.js": "^4.3.0",
"url-parse": "^1.4.6",
"vue": "^2.6.11",
"vue-class-component": "^7.2.6",
"vue-clipboard2": "^0.3.0",
"vue-codemirror": "^4.0.6",
"vue-i18n": "^8.24.4",
"vue-infinite-loading": "^2.4.4",
"vue-json-editor": "^1.4.3",
"vue-property-decorator": "^9.1.2",
"vue-router": "^3.1.3",
"vue-showdown": "^2.4.1",
"vuex": "^3.0.1",
"vuex-persistedstate": "^4.0.0-beta.3",
"xregexp": "^4.2.4",
"xterm": "^4.9.0",
"xterm-addon-fit": "^0.4.0"
},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"@fortawesome/fontawesome-free": "^5.11.2",
"@openapitools/openapi-generator-cli": "^1.0.12-4.3.0",
"@types/jquery": "^3.5.1",
"@types/lodash": "^4.14.123",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/eslint-config-typescript": "^4.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-dynamic-import-node": "^2.3.3",
"echarts": "^4.8.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"imports-loader": "^1.1.0",
"lint-staged": "^8.2.1",
"node-sass": "^4.14.1",
"sass-loader": "^7.1.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"ts-loader": "^7.0.0",
"typescript": "^3.8.3",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
window.g = {
baseURL: 'http://218.94.122.141:8089/api',
defaultLang: 'zh-CN', // 目前支持 zh-CN / ja-JP
}
\ No newline at end of file
No preview for this file type
<!DOCTYPE html>
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>SeetaAIoT</title>
</head>
<body>
<script type="text/javascript" src="./config.js"></script>
<script src="<%= BASE_URL %>webuploader/jquery.min.js"></script>
<script src="<%= BASE_URL %>webuploader/webuploader.min.js"></script>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
This diff could not be displayed because it is too large.
import lodash from 'lodash'
import momentIns from 'moment'
import toast from '@/helpers/toast'
declare global {
const _: typeof lodash
const moment: typeof momentIns
const echarts: any
const vm: any
const routerRefresh: Function
const Toast: {
success,
danger,
warning,
info,
}
const DOMPurify: any
const WebUploader: any
interface Window {
Vue: any,
vm: any,
routerRefresh: Function,
XRegExp: any,
_: typeof lodash,
moment: typeof momentIns,
echarts: any,
Toast: {
success,
danger,
warning,
},
DOMPurify: any,
}
}
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
// 训练平台系统host信息
declare const productionHost: {
baseUrl,
rootUrl,
jupyterRootUrl,
baseWebSocket,
apiTestUrl,
adminUrl,
mockUrl,
}
// 推理平台系统host信息
declare const inferenceHost: {
baseUrl,
adminUrl,
baseWebSocket,
}
\ No newline at end of file
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
data() {
return {
formatData: [
{
label: 'test',
key: 'name',
type: 'input',
required: true,
validator: ['notEmpty', 'min2', 'max64', 'normName'],
placeholder: 'test',
},
],
}
},
}
</script>
<style>
#app {
font-family: PingFangSC-Regular, PingFang SC, Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
height: 100%;
width: 100%;
}
/* 自定义进度条颜色 */
#nprogress .bar {
background: #409EFF !important;
height: 5px !important;
}
</style>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1598514256546" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="646" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><defs><style type="text/css"></style></defs><path d="M621.714286 248.685714a373.028571 373.028571 0 0 1 15.798857 745.764572L621.714286 994.742857H146.285714a43.885714 43.885714 0 0 1-7.094857-87.186286L146.285714 906.971429h475.428572a285.257143 285.257143 0 0 0 14.628571-570.148572L621.714286 336.457143H146.285714a43.885714 43.885714 0 0 1-7.094857-87.186286L146.285714 248.685714h475.428572z" fill="#ABABAB" p-id="647"></path><path d="M323.584 54.637714a43.885714 43.885714 0 0 1 4.681143 56.612572l-4.681143 5.485714L147.748571 292.571429l175.835429 175.835428a43.885714 43.885714 0 0 1 4.681143 56.612572l-4.681143 5.485714a43.885714 43.885714 0 0 1-56.612571 4.608l-5.412572-4.608L54.637714 323.584a43.885714 43.885714 0 0 1-4.608-56.612571l4.608-5.412572L261.558857 54.637714a43.885714 43.885714 0 0 1 62.025143 0z" fill="#ABABAB" p-id="648"></path></svg>
\ No newline at end of file
This diff could not be displayed because it is too large.
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100%" height="100%" viewBox="0 0 912 467" preserveAspectRatio="none meet" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>frame2</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="frame2" fill-rule="nonzero">
<rect id="矩形备份-2" stroke="#A1A3A5" stroke-width="2" fill="#333333" opacity="0.5" x="1" y="1" width="910" height="465"></rect>
<g id="角备份-3" fill="#8E8E8E">
<g id="编组">
<g id="编组-5">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
<g id="编组-5备份" transform="translate(5.500000, 461.500000) rotate(-90.000000) translate(-5.500000, -461.500000) translate(0.000000, 456.000000)">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
</g>
<g id="编组备份" transform="translate(906.500000, 233.500000) rotate(-180.000000) translate(-906.500000, -233.500000) translate(901.000000, 0.000000)">
<g id="编组-5">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
<g id="编组-5备份" transform="translate(5.500000, 461.500000) rotate(-90.000000) translate(-5.500000, -461.500000) translate(0.000000, 456.000000)">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100%" height="100%" viewBox="0 0 912 478" preserveAspectRatio="none meet" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>frame1</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="frame1" fill-rule="nonzero">
<g id="编组-2" transform="translate(0.000000, 11.000000)">
<rect id="矩形" stroke="#A1A3A5" stroke-width="2" fill="#333333" opacity="0.5" x="1" y="1" width="910" height="465"></rect>
<g id="角备份" fill="#8E8E8E">
<g id="编组">
<g id="编组-5">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
<g id="编组-5备份" transform="translate(5.500000, 461.500000) rotate(-90.000000) translate(-5.500000, -461.500000) translate(0.000000, 456.000000)">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
</g>
<g id="编组备份" transform="translate(906.500000, 233.500000) rotate(-180.000000) translate(-906.500000, -233.500000) translate(901.000000, 0.000000)">
<g id="编组-5">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
<g id="编组-5备份" transform="translate(5.500000, 461.500000) rotate(-90.000000) translate(-5.500000, -461.500000) translate(0.000000, 456.000000)">
<rect id="矩形" x="0" y="0" width="11" height="3"></rect>
<rect id="矩形备份-4" transform="translate(1.500000, 5.500000) rotate(90.000000) translate(-1.500000, -5.500000) " x="-4" y="4" width="11" height="3"></rect>
</g>
</g>
</g>
</g>
<g id="_编组_" transform="translate(230.000000, 0.000000)" fill="#8E8E8E">
<polygon id="_路径_" points="249.28 29.33 202.93 29.33 137.48 29.33 109.04 0.89 0.68 0.89 0.68 1.94 108.61 1.94 137.05 30.38 202.93 30.38 249.28 30.38 311.29 30.38 339.74 1.94 451.53 1.94 451.53 0.89 339.3 0.89 310.86 29.33"></polygon>
<polygon id="_路径_2" points="309.54 20.55 230.23 20.55 221.98 20.55 142.66 20.55 122.65 0.54 121.9 1.28 142.23 21.6 221.98 21.6 230.23 21.6 309.98 21.6 329.82 1.77 329.07 1.02"></polygon>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="6px" height="12px" viewBox="0 0 6 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>short line3</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-1038.000000, -200.000000)" fill="#66B3FF" fill-rule="nonzero">
<g id="short-line3" transform="translate(1038.000000, 200.000000)">
<rect id="_矩形_23" x="0" y="0" width="6" height="2"></rect>
<rect id="_矩形_24" x="0" y="4" width="6" height="2"></rect>
<rect id="_矩形_25" x="0" y="7" width="6" height="2"></rect>
<rect id="_矩形_26" x="0" y="10" width="6" height="2"></rect>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="16px" viewBox="0 0 12 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>one</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-100.000000, -697.000000)">
<g id="编组-22" transform="translate(73.000000, 659.000000)">
<g id="one" transform="translate(25.000000, 38.000000)">
<rect id="矩形" fill="#000000" fill-rule="nonzero" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M4.971875,6.425 L7.996875,9.45 L13.6375,3.809375 C13.7578125,3.6890625 13.8234375,3.525 13.81875,3.3546875 L13.7640625,1.1984375 C13.7546875,0.8609375 13.4796875,0.5921875 13.1421875,0.5921875 L11.0578125,0.5953125 C10.89375,0.5953125 10.7359375,0.6609375 10.61875,0.778125 L4.971875,6.425 Z" id="路径" fill="#D32F2F" fill-rule="nonzero"></path>
<path d="M11.028125,6.4265625 L8.003125,9.4515625 L2.3625,3.8109375 C2.2421875,3.690625 2.1765625,3.5265625 2.18125,3.35625 L2.2359375,1.2 C2.2453125,0.8625 2.5203125,0.59375 2.8578125,0.59375 L4.9421875,0.596875 C5.10625,0.596875 5.2640625,0.6625 5.38125,0.7796875 L11.028125,6.4265625 L11.028125,6.4265625 Z" id="路径" fill="#F44336" fill-rule="nonzero"></path>
<path d="M2.8609375,10.2640625 C2.8609375,13.1048772 5.16387282,15.4078125 8.0046875,15.4078125 C10.8455022,15.4078125 13.1484375,13.1048772 13.1484375,10.2640625 C13.1484375,7.42324782 10.8455022,5.1203125 8.0046875,5.1203125 C5.16387282,5.1203125 2.8609375,7.42324782 2.8609375,10.2640625 Z" id="路径" fill="#FFDF34" fill-rule="nonzero"></path>
<path d="M10.0203125,8.4765625 L11.50625,10.465625 C11.509375,10.3984375 11.5125,10.33125 11.5125,10.2640625 C11.5125,8.3265625 9.9421875,6.75625 8.0046875,6.75625 C6.0671875,6.75625 4.496875,8.3265625 4.496875,10.2640625 C4.496875,11.9203125 5.6453125,13.3078125 7.1875,13.6765625 L5.709375,11.7 L10.0203125,8.4765625 Z" id="路径" fill="#E9B82E" fill-rule="nonzero"></path>
<path d="M11.50625,10.465625 C11.4015625,12.309375 9.8734375,13.7703125 8.0046875,13.7703125 C7.7234375,13.7703125 7.45,13.7375 7.1890625,13.675 L8.46875,15.3875 C10.4328125,15.2125 12.0796875,13.9328125 12.7828125,12.1734375 L11.50625,10.465625 Z" id="路径" fill="#FFDF34" fill-rule="nonzero"></path>
<path d="M11.50625,10.465625 L10.0203125,8.4765625 L5.7109375,11.6984375 L7.1890625,13.675 C7.4515625,13.7375 7.7234375,13.7703138 8.0046875,13.7703138 C9.875,13.771875 11.4015625,12.309375 11.50625,10.465625 L11.50625,10.465625 Z" id="路径" fill="#E9B82E" fill-rule="nonzero"></path>
<path d="M5.315625,10.2640625 C5.315625,11.224772 5.8281574,12.1125042 6.66015624,12.592859 C7.49215508,13.0732137 8.51721992,13.0732137 9.34921876,12.592859 C10.1812176,12.1125042 10.69375,11.224772 10.69375,10.2640625 C10.69375,8.77893429 9.48981571,7.575 8.0046875,7.575 C6.51955929,7.575 5.315625,8.77893429 5.315625,10.2640625 L5.315625,10.2640625 Z" id="路径" fill="#FFDF34" fill-rule="nonzero"></path>
<text id="1" font-family="Digital-7Mono, Digital-7 Mono" font-size="6" font-weight="normal" fill="#FFFFFF">
<tspan x="6" y="12.3">1</tspan>
</text>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="34px" height="35px" viewBox="0 0 34 35" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>编组 20</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="0%" y1="50%" x2="100%" y2="50%" id="linearGradient-1">
<stop stop-color="#FFD42E" offset="0%"></stop>
<stop stop-color="#FF4200" offset="100%"></stop>
</linearGradient>
<linearGradient x1="0%" y1="50%" x2="100%" y2="50%" id="linearGradient-2">
<stop stop-color="#FFD42E" offset="0%"></stop>
<stop stop-color="#FF4200" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-298.000000, -213.000000)" fill-rule="nonzero">
<g id="编组-20" transform="translate(298.182506, 213.288180)">
<circle id="椭圆形" fill="url(#linearGradient-1)" cx="27.8770686" cy="28.1917258" r="2.60047281"></circle>
<path d="M33.6501182,30.3397163 L33.7411348,33.9257683 L30.1550827,33.8347518 L25.7940898,27.9264775 C25.369561,27.353323 24.9018475,26.8134529 24.3950355,26.3115839 L-1.59872116e-13,-1.0658141e-14 L-1.59872116e-13,-1.0658141e-14 L26.0879433,24.6186761 C26.5926546,25.1222455 27.1351344,25.586483 27.7106383,26.0073286 L33.6501182,30.3397163 Z" id="路径" fill="url(#linearGradient-2)"></path>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="110px" height="88px" viewBox="0 0 110 88" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>编组 16</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="-8.11677788e-15%" y1="50.0158178%" x2="100%" y2="50.0158178%" id="linearGradient-1">
<stop stop-color="#FFD42E" offset="0%"></stop>
<stop stop-color="#FF4200" offset="100%"></stop>
</linearGradient>
<linearGradient x1="5.55150901e-15%" y1="50.0078924%" x2="99.9881738%" y2="50.0078924%" id="linearGradient-2">
<stop stop-color="#FFD42E" offset="0%"></stop>
<stop stop-color="#FF4200" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-1.32998172e-13%" y1="50%" x2="99.9766027%" y2="50%" id="linearGradient-3">
<stop stop-color="#FFD42E" offset="0%"></stop>
<stop stop-color="#FF4200" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-269.000000, -187.000000)" fill-rule="nonzero">
<g id="编组-16" transform="translate(269.000000, 187.247665)">
<path d="M8.45673759,84.3613183 C7.93664303,83.5447698 7.43995272,82.7048171 6.96666667,81.8622639 L17.1761229,76.1412237 C17.5479905,76.8043443 17.9406619,77.4674648 18.3489362,78.1097816 L8.45673759,84.3613183 Z" id="路径" fill="url(#linearGradient-1)"></path>
<path d="M104.749645,78.4426421 L94.165721,73.4523348 C94.8231953,72.0558716 95.405663,70.6253103 95.9106383,69.1667556 L106.970449,72.9894506 C106.329108,74.8459168 105.587756,76.6663098 104.749645,78.4426421 Z M3.80969267,75.2180558 C3.08839973,73.3918061 2.46598537,71.5280354 1.94515366,69.6348407 L13.2286052,66.5298762 C13.6370955,68.0175597 14.1258328,69.4820356 14.6926714,70.9168738 L3.80969267,75.2180558 Z M108.9,65.8927603 L97.4267139,63.5887414 C97.7315755,62.074501 97.9546724,60.544942 98.0950355,59.0067083 L109.739953,60.0833041 C109.564146,62.033427 109.283758,63.9727037 108.9,65.8927603 L108.9,65.8927603 Z M0.475886525,62.4237296 C0.213167432,60.4832421 0.0543121834,58.5301038 1.27897692e-13,56.5726658 L11.7021277,56.2450062 C11.7441994,57.7898934 11.8674585,59.3315006 12.0713948,60.8634459 L0.475886525,62.4237296 Z M98.2536643,53.2050535 C98.1887072,51.6608368 98.0437531,50.1210249 97.8193853,48.5918147 L109.39409,46.8755027 C109.680881,48.8147741 109.864905,50.7678565 109.94539,52.7265665 L98.2536643,53.2050535 Z M11.9179669,50.4615547 L0.278250591,49.2393324 C0.481639194,47.2911424 0.789808133,45.3553207 1.20141844,43.4402781 L12.6434988,45.8951244 C12.3196045,47.4030928 12.0774077,48.9274572 11.9179669,50.4615547 L11.9179669,50.4615547 Z M96.5763593,42.9591906 C96.1456399,41.4771222 95.636973,40.0188278 95.0524823,38.5903963 L105.878251,34.1487887 C106.622589,35.9644666 107.269296,37.8186508 107.815603,39.7033986 L96.5763593,42.9591906 Z M14.2323877,40.3197107 L3.22458629,36.3539897 C3.89163361,34.5081862 4.65638606,32.6991691 5.51560284,30.9346043 L16.0345154,36.0627367 C15.359225,37.4489865 14.7576767,38.86996 14.2323877,40.3197107 L14.2323877,40.3197107 Z M92.5066194,33.3920511 C91.733226,32.0560846 90.8885878,30.762651 89.9763593,29.5173466 L99.429078,22.6182923 C100.584203,24.2031094 101.6545,25.8480497 102.635461,27.5461882 L92.5066194,33.3920511 Z M18.9132388,31.0516256 L9.17446809,24.5504436 C10.2615184,22.9198315 11.4351467,21.348628 12.6903073,19.8435878 L21.6827423,27.330349 C20.6939467,28.5178478 19.769455,29.7574479 18.9132388,31.0438242 L18.9132388,31.0516256 Z M86.2654846,25.0887414 C85.1961923,23.9714878 84.0676972,22.9124385 82.88487,21.9161646 L90.4262411,12.9731386 C91.9240478,14.2374206 93.3537171,15.5802854 94.7092199,16.99607 L86.2654846,25.0887414 Z M25.6744681,23.1435878 L17.7534279,14.5230204 C19.1964286,13.1966932 20.7085471,11.9475895 22.2834515,10.78094 L29.2475177,20.1868502 C28.0049964,21.1057035 26.8122909,22.0900762 25.6744681,23.1357863 L25.6744681,23.1435878 Z M78.2248227,18.4757391 C76.9224768,17.6464421 75.5770042,16.886901 74.1940898,16.2003253 L79.3950355,5.71521897 C81.1515777,6.58917649 82.8607353,7.55529754 84.5153664,8.60954521 L78.2248227,18.4757391 Z M34.1156028,17.0610819 L28.4647754,6.81261849 C30.1842726,5.86788837 31.9524553,5.01461222 33.7619385,4.25635372 L38.2789598,15.0509164 C36.8562636,15.6461219 35.4664853,16.3171391 34.1156028,17.0610819 L34.1156028,17.0610819 Z M68.8501182,13.982122 C67.3885409,13.4884321 65.9015349,13.073494 64.3955083,12.739096 L66.9283688,1.31261849 C68.8449907,1.73776876 70.7373935,2.26555816 72.5973995,2.89370596 L68.8501182,13.982122 Z M43.7477541,13.1837769 L40.7208038,1.87952157 C42.617186,1.37343377 44.5390224,0.968059965 46.4782506,0.665100763 L48.2751773,12.2346043 C46.7501226,12.4696632 45.2388022,12.7865109 43.7477541,13.1837769 L43.7477541,13.1837769 Z M58.6770686,11.8835405 C57.1364624,11.7453084 55.5896089,11.6888856 54.043026,11.7145097 L53.7829787,0.0123820865 C55.741284,-0.0288169253 57.700402,0.0328123958 59.6522459,0.197015656 L58.6770686,11.8835405 Z" id="形状" fill="url(#linearGradient-2)"></path>
<path d="M99.4966903,87.3154554 L90.0309693,80.4372048 C90.4782506,79.8208927 90.91513,79.1863774 91.3312057,78.546661 L101.14539,84.9178194 C100.617494,85.729167 100.063593,86.5353135 99.4966903,87.3154554 Z" id="路径" fill="url(#linearGradient-3)"></path>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="105px" height="20px" viewBox="0 0 105 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>short line1</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="730.856649%" y1="-1290.13499%" x2="-1077.71405%" y2="2269.73001%" id="linearGradient-1">
<stop stop-color="#27AAE1" offset="0%"></stop>
<stop stop-color="#44BBE9" offset="17%"></stop>
<stop stop-color="#69D0F2" offset="41%"></stop>
<stop stop-color="#83DFF9" offset="64%"></stop>
<stop stop-color="#93E9FE" offset="84%"></stop>
<stop stop-color="#99ECFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="710.15599%" y1="-1249.37695%" x2="-1098.41471%" y2="2310.48806%" id="linearGradient-2">
<stop stop-color="#27AAE1" offset="0%"></stop>
<stop stop-color="#44BBE9" offset="17%"></stop>
<stop stop-color="#69D0F2" offset="41%"></stop>
<stop stop-color="#83DFF9" offset="64%"></stop>
<stop stop-color="#93E9FE" offset="84%"></stop>
<stop stop-color="#99ECFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="689.45533%" y1="-1208.6189%" x2="-1119.11537%" y2="2351.24611%" id="linearGradient-3">
<stop stop-color="#27AAE1" offset="0%"></stop>
<stop stop-color="#44BBE9" offset="17%"></stop>
<stop stop-color="#69D0F2" offset="41%"></stop>
<stop stop-color="#83DFF9" offset="64%"></stop>
<stop stop-color="#93E9FE" offset="84%"></stop>
<stop stop-color="#99ECFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="668.733697%" y1="-1167.91277%" x2="-1139.81603%" y2="2392.00415%" id="linearGradient-4">
<stop stop-color="#27AAE1" offset="0%"></stop>
<stop stop-color="#44BBE9" offset="17%"></stop>
<stop stop-color="#69D0F2" offset="41%"></stop>
<stop stop-color="#83DFF9" offset="64%"></stop>
<stop stop-color="#93E9FE" offset="84%"></stop>
<stop stop-color="#99ECFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="648.033038%" y1="-1127.15472%" x2="-1160.51669%" y2="2432.7622%" id="linearGradient-5">
<stop stop-color="#27AAE1" offset="0%"></stop>
<stop stop-color="#44BBE9" offset="17%"></stop>
<stop stop-color="#69D0F2" offset="41%"></stop>
<stop stop-color="#83DFF9" offset="64%"></stop>
<stop stop-color="#93E9FE" offset="84%"></stop>
<stop stop-color="#99ECFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="627.353351%" y1="-1086.39668%" x2="-1181.21735%" y2="2473.52025%" id="linearGradient-6">
<stop stop-color="#27AAE1" offset="0%"></stop>
<stop stop-color="#44BBE9" offset="17%"></stop>
<stop stop-color="#69D0F2" offset="41%"></stop>
<stop stop-color="#83DFF9" offset="64%"></stop>
<stop stop-color="#93E9FE" offset="84%"></stop>
<stop stop-color="#99ECFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="606.631718%" y1="-1045.63863%" x2="-1201.93898%" y2="2514.22638%" id="linearGradient-7">
<stop stop-color="#27AAE1" offset="0%"></stop>
<stop stop-color="#44BBE9" offset="17%"></stop>
<stop stop-color="#69D0F2" offset="41%"></stop>
<stop stop-color="#83DFF9" offset="64%"></stop>
<stop stop-color="#93E9FE" offset="84%"></stop>
<stop stop-color="#99ECFF" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-39.000000, -65.000000)" fill-rule="nonzero">
<g id="short-line1" transform="translate(39.000000, 65.000000)">
<g id="_编组_12" transform="translate(96.000000, 0.000000)" fill="url(#linearGradient-1)">
<rect id="_矩形_8" transform="translate(4.560000, 9.630000) rotate(180.000000) translate(-4.560000, -9.630000) " x="0.67" y="1.24344979e-14" width="7.78" height="19.26"></rect>
</g>
<g id="_编组_13" transform="translate(80.000000, 0.000000)" fill="url(#linearGradient-2)">
<rect id="_矩形_9" transform="translate(4.460000, 9.630000) rotate(180.000000) translate(-4.460000, -9.630000) " x="0.57" y="1.0658141e-14" width="7.78" height="19.26"></rect>
</g>
<g id="_编组_14" transform="translate(64.000000, 0.000000)" fill="url(#linearGradient-3)">
<rect id="_矩形_10" transform="translate(4.340000, 9.630000) rotate(180.000000) translate(-4.340000, -9.630000) " x="0.45" y="8.8817842e-15" width="7.78" height="19.26"></rect>
</g>
<g id="_编组_15" transform="translate(48.000000, 0.000000)" fill="url(#linearGradient-4)">
<rect id="_矩形_11" transform="translate(4.230000, 9.630000) rotate(180.000000) translate(-4.230000, -9.630000) " x="0.34" y="7.10542736e-15" width="7.78" height="19.26"></rect>
</g>
<g id="_编组_16" opacity="0.35" transform="translate(32.000000, 0.000000)" fill="url(#linearGradient-5)">
<rect id="_矩形_12" transform="translate(4.110000, 9.630000) rotate(180.000000) translate(-4.110000, -9.630000) " x="0.22" y="3.55271368e-15" width="7.78" height="19.26"></rect>
</g>
<g id="_编组_17" opacity="0.35" transform="translate(16.000000, 0.000000)" fill="url(#linearGradient-6)">
<rect id="_矩形_13" transform="translate(4.000000, 9.630000) rotate(180.000000) translate(-4.000000, -9.630000) " x="0.11" y="1.77635684e-15" width="7.78" height="19.26"></rect>
</g>
<g id="_编组_18" opacity="0.35" fill="url(#linearGradient-7)">
<rect id="_矩形_14" transform="translate(3.890000, 9.630000) rotate(180.000000) translate(-3.890000, -9.630000) " x="-1.33226763e-15" y="0" width="7.78" height="19.26"></rect>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="16px" viewBox="0 0 12 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>two</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-100.000000, -723.000000)">
<g id="编组-22" transform="translate(73.000000, 659.000000)">
<g id="two" transform="translate(25.000000, 64.000000)">
<g id="奖牌" fill-rule="nonzero">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M4.971875,6.425 L7.996875,9.45 L13.6375,3.809375 C13.7578125,3.6890625 13.8234375,3.525 13.81875,3.3546875 L13.7640625,1.1984375 C13.7546875,0.8609375 13.4796875,0.5921875 13.1421875,0.5921875 L11.0578125,0.5953125 C10.89375,0.5953125 10.7359375,0.6609375 10.61875,0.778125 L4.971875,6.425 Z" id="路径" fill="#D32F2F"></path>
<path d="M11.028125,6.4265625 L8.003125,9.4515625 L2.3625,3.8109375 C2.2421875,3.690625 2.1765625,3.5265625 2.18125,3.35625 L2.2359375,1.2 C2.2453125,0.8625 2.5203125,0.59375 2.8578125,0.59375 L4.9421875,0.596875 C5.10625,0.596875 5.2640625,0.6625 5.38125,0.7796875 L11.028125,6.4265625 L11.028125,6.4265625 Z" id="路径" fill="#F44336"></path>
<path d="M2.8609375,10.2640625 C2.8609375,13.1048772 5.16387282,15.4078125 8.0046875,15.4078125 C10.8455022,15.4078125 13.1484375,13.1048772 13.1484375,10.2640625 C13.1484375,7.42324782 10.8455022,5.1203125 8.0046875,5.1203125 C5.16387282,5.1203125 2.8609375,7.42324782 2.8609375,10.2640625 Z" id="路径" fill="#CDCDCD"></path>
<path d="M10.0203125,8.4765625 L11.50625,10.465625 C11.509375,10.3984375 11.5125,10.33125 11.5125,10.2640625 C11.5125,8.3265625 9.9421875,6.75625 8.0046875,6.75625 C6.0671875,6.75625 4.496875,8.3265625 4.496875,10.2640625 C4.496875,11.9203125 5.6453125,13.3078125 7.1875,13.6765625 L5.709375,11.7 L10.0203125,8.4765625 Z" id="路径" fill="#8A8A8A"></path>
<path d="M11.50625,10.465625 C11.4015625,12.309375 9.8734375,13.7703125 8.0046875,13.7703125 C7.7234375,13.7703125 7.45,13.7375 7.1890625,13.675 L8.46875,15.3875 C10.4328125,15.2125 12.0796875,13.9328125 12.7828125,12.1734375 L11.50625,10.465625 Z" id="路径" fill="#CDCDCD"></path>
<path d="M11.50625,10.465625 L10.0203125,8.4765625 L5.7109375,11.6984375 L7.1890625,13.675 C7.4515625,13.7375 7.7234375,13.7703138 8.0046875,13.7703138 C9.875,13.771875 11.4015625,12.309375 11.50625,10.465625 L11.50625,10.465625 Z" id="路径" fill="#8A8A8A"></path>
<path d="M5.315625,10.2640625 C5.315625,11.224772 5.8281574,12.1125042 6.66015624,12.592859 C7.49215508,13.0732137 8.51721992,13.0732137 9.34921876,12.592859 C10.1812176,12.1125042 10.69375,11.224772 10.69375,10.2640625 C10.69375,8.77893429 9.48981571,7.575 8.0046875,7.575 C6.51955929,7.575 5.315625,8.77893429 5.315625,10.2640625 L5.315625,10.2640625 Z" id="路径" fill="#CDCDCD"></path>
</g>
<text id="2" font-family="Digital-7Mono, Digital-7 Mono" font-size="6" font-weight="normal" fill="#FFFFFF">
<tspan x="7" y="12">2</tspan>
</text>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>server</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="server" fill-rule="nonzero">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="18" height="18"></rect>
<path d="M16.4330859,1.08966797 C16.5397852,1.08966797 16.6310156,1.18107422 16.6310156,1.28759766 L16.6310156,4.93804688 C16.6310156,5.04474609 16.5396094,5.13597656 16.4330859,5.13597656 L1.60066406,5.13597656 C1.49396484,5.13597656 1.40273437,5.04457031 1.40273437,4.93804688 L1.40273437,1.28759766 C1.40273437,1.18089844 1.49414062,1.08966797 1.60066406,1.08966797 L16.4330859,1.08966797 M16.4330859,0.00193359375 L1.60066406,0.00193359375 C0.893671875,0.00193359375 0.315,0.580605469 0.315,1.28759766 L0.315,4.93804687 C0.315,5.64503906 0.893671875,6.22371094 1.60066406,6.22371094 L16.4330859,6.22371094 C17.1400781,6.22371094 17.71875,5.64503906 17.71875,4.93804687 L17.71875,1.28759766 C17.71875,0.580605469 17.1400781,0.00193359375 16.4330859,0.00193359375 Z" id="形状" fill="#66B3FF"></path>
<path d="M4.19818359,2.52984375 L2.89283203,2.52984375 L2.89283203,3.83519531 L4.19818359,3.83519531 L4.19818359,2.52984375 L4.19818359,2.52984375 Z M6.43886719,2.52984375 L5.13351563,2.52984375 L5.13351563,3.83519531 L6.43886719,3.83519531 L6.43886719,2.52984375 L6.43886719,2.52984375 Z M15.4126758,2.52984375 L10.7354883,2.52984375 L10.7354883,3.83519531 L15.4128516,3.83519531 L15.4128516,2.52984375 L15.4126758,2.52984375 Z M16.4330859,6.96339844 C16.5397852,6.96339844 16.6310156,7.05480469 16.6310156,7.16132813 L16.6310156,10.8117773 C16.6310156,10.9184766 16.5396094,11.009707 16.4330859,11.009707 L1.60066406,11.009707 C1.49396484,11.009707 1.40273437,10.9183008 1.40273437,10.8117773 L1.40273437,7.16150391 C1.40273437,7.05480469 1.49414062,6.96357422 1.60066406,6.96357422 L16.4330859,6.96357422 M16.4330859,5.87566406 L1.60066406,5.87566406 C0.893671875,5.87566406 0.315,6.45433594 0.315,7.16132813 L0.315,10.8117773 C0.315,11.5187695 0.893671875,12.0974414 1.60066406,12.0974414 L16.4330859,12.0974414 C17.1400781,12.0974414 17.71875,11.5187695 17.71875,10.8117773 L17.71875,7.16150391 C17.71875,6.45433594 17.1400781,5.87566406 16.4330859,5.87566406 Z" id="形状" fill="#66B3FF"></path>
<path d="M4.19818359,8.40357422 L2.89283203,8.40357422 L2.89283203,9.70892578 L4.19818359,9.70892578 L4.19818359,8.40357422 L4.19818359,8.40357422 Z M6.43886719,8.40357422 L5.13351563,8.40357422 L5.13351563,9.70892578 L6.43886719,9.70892578 L6.43886719,8.40357422 L6.43886719,8.40357422 Z M15.4126758,8.40357422 L10.7354883,8.40357422 L10.7354883,9.70892578 L15.4128516,9.70892578 L15.4128516,8.40357422 L15.4126758,8.40357422 Z M16.4330859,12.8373047 C16.5397852,12.8373047 16.6310156,12.9287109 16.6310156,13.0352344 L16.6310156,16.6856836 C16.6310156,16.7923828 16.5396094,16.8836133 16.4330859,16.8836133 L1.60066406,16.8836133 C1.49396484,16.8836133 1.40273437,16.792207 1.40273437,16.6856836 L1.40273437,13.0352344 C1.40273437,12.9285352 1.49414062,12.8373047 1.60066406,12.8373047 L16.4330859,12.8373047 M16.4330859,11.7495703 L1.60066406,11.7495703 C0.893671875,11.7495703 0.315,12.3282422 0.315,13.0352344 L0.315,16.6856836 C0.315,17.3926758 0.893671875,17.9713477 1.60066406,17.9713477 L16.4330859,17.9713477 C17.1400781,17.9713477 17.71875,17.3926758 17.71875,16.6856836 L17.71875,13.0352344 C17.71875,12.3282422 17.1400781,11.7495703 16.4330859,11.7495703 Z" id="形状" fill="#66B3FF"></path>
<path d="M4.19818359,14.2774805 L2.89283203,14.2774805 L2.89283203,16.0774805 L4.19818359,16.0774805 L4.19818359,14.2774805 L4.19818359,14.2774805 Z M6.43886719,14.2774805 L5.13351562,14.2774805 L5.13351562,16.0774805 L6.43886719,16.0774805 L6.43886719,14.2774805 Z M15.4126758,14.2774805 L10.7354883,14.2774805 L10.7354883,16.0774805 L15.4128516,16.0774805 L15.4128516,14.2774805 L15.4126758,14.2774805 Z" id="形状" fill="#66B3FF"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100%" height="6px" viewBox="0 0 118 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>short line2</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-93.000000, -159.000000)" fill="#66B3FF" fill-rule="nonzero">
<g id="short-line2" transform="translate(93.000000, 159.000000)">
<g id="编组-32">
<rect id="_矩形_27" opacity="0.5" transform="translate(1.500000, 3.000000) rotate(-90.000000) translate(-1.500000, -3.000000) " x="0" y="2" width="3" height="2"></rect>
<rect id="_矩形_28" opacity="0.8" transform="translate(5.000000, 3.000000) rotate(-90.000000) translate(-5.000000, -3.000000) " x="3" y="2" width="4" height="2"></rect>
<rect id="_矩形_29" opacity="0.8" transform="translate(8.500000, 3.000000) rotate(-90.000000) translate(-8.500000, -3.000000) " x="6" y="2" width="5" height="2"></rect>
<rect id="_矩形_30" transform="translate(12.000000, 3.000000) rotate(-90.000000) translate(-12.000000, -3.000000) " x="9" y="2" width="6" height="2"></rect>
</g>
<g id="编组-32备份" transform="translate(111.500000, 3.000000) rotate(-180.000000) translate(-111.500000, -3.000000) translate(105.000000, 0.000000)">
<rect id="_矩形_27" opacity="0.5" transform="translate(1.500000, 3.000000) rotate(-90.000000) translate(-1.500000, -3.000000) " x="0" y="2" width="3" height="2"></rect>
<rect id="_矩形_28" opacity="0.8" transform="translate(5.000000, 3.000000) rotate(-90.000000) translate(-5.000000, -3.000000) " x="3" y="2" width="4" height="2"></rect>
<rect id="_矩形_29" opacity="0.8" transform="translate(8.500000, 3.000000) rotate(-90.000000) translate(-8.500000, -3.000000) " x="6" y="2" width="5" height="2"></rect>
<rect id="_矩形_30" transform="translate(12.000000, 3.000000) rotate(-90.000000) translate(-12.000000, -3.000000) " x="9" y="2" width="6" height="2"></rect>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="16px" viewBox="0 0 12 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>three</title>
<desc>Created with Sketch.</desc>
<g id="Seetass-大屏" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="SeeTaas大屏" transform="translate(-100.000000, -749.000000)">
<g id="编组-22" transform="translate(73.000000, 659.000000)">
<g id="three" transform="translate(25.000000, 90.000000)">
<g id="奖牌备份-2" fill-rule="nonzero">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M4.971875,6.425 L7.996875,9.45 L13.6375,3.809375 C13.7578125,3.6890625 13.8234375,3.525 13.81875,3.3546875 L13.7640625,1.1984375 C13.7546875,0.8609375 13.4796875,0.5921875 13.1421875,0.5921875 L11.0578125,0.5953125 C10.89375,0.5953125 10.7359375,0.6609375 10.61875,0.778125 L4.971875,6.425 Z" id="路径" fill="#D32F2F"></path>
<path d="M11.028125,6.4265625 L8.003125,9.4515625 L2.3625,3.8109375 C2.2421875,3.690625 2.1765625,3.5265625 2.18125,3.35625 L2.2359375,1.2 C2.2453125,0.8625 2.5203125,0.59375 2.8578125,0.59375 L4.9421875,0.596875 C5.10625,0.596875 5.2640625,0.6625 5.38125,0.7796875 L11.028125,6.4265625 L11.028125,6.4265625 Z" id="路径" fill="#F44336"></path>
<path d="M2.8609375,10.2640625 C2.8609375,13.1048772 5.16387282,15.4078125 8.0046875,15.4078125 C10.8455022,15.4078125 13.1484375,13.1048772 13.1484375,10.2640625 C13.1484375,7.42324782 10.8455022,5.1203125 8.0046875,5.1203125 C5.16387282,5.1203125 2.8609375,7.42324782 2.8609375,10.2640625 Z" id="路径" fill="#F5A74A"></path>
<path d="M10.0203125,8.4765625 L11.50625,10.465625 C11.509375,10.3984375 11.5125,10.33125 11.5125,10.2640625 C11.5125,8.3265625 9.9421875,6.75625 8.0046875,6.75625 C6.0671875,6.75625 4.496875,8.3265625 4.496875,10.2640625 C4.496875,11.9203125 5.6453125,13.3078125 7.1875,13.6765625 L5.709375,11.7 L10.0203125,8.4765625 Z" id="路径" fill="#E98952"></path>
<path d="M11.50625,10.465625 C11.4015625,12.309375 9.8734375,13.7703125 8.0046875,13.7703125 C7.7234375,13.7703125 7.45,13.7375 7.1890625,13.675 L8.46875,15.3875 C10.4328125,15.2125 12.0796875,13.9328125 12.7828125,12.1734375 L11.50625,10.465625 Z" id="路径" fill="#F5A74A"></path>
<path d="M11.50625,10.465625 L10.0203125,8.4765625 L5.7109375,11.6984375 L7.1890625,13.675 C7.4515625,13.7375 7.7234375,13.7703138 8.0046875,13.7703138 C9.875,13.771875 11.4015625,12.309375 11.50625,10.465625 L11.50625,10.465625 Z" id="路径" fill="#E98952"></path>
<path d="M5.315625,10.2640625 C5.315625,11.224772 5.8281574,12.1125042 6.66015624,12.592859 C7.49215508,13.0732137 8.51721992,13.0732137 9.34921876,12.592859 C10.1812176,12.1125042 10.69375,11.224772 10.69375,10.2640625 C10.69375,8.77893429 9.48981571,7.575 8.0046875,7.575 C6.51955929,7.575 5.315625,8.77893429 5.315625,10.2640625 L5.315625,10.2640625 Z" id="路径" fill="#F5A74A"></path>
</g>
<text id="3" font-family="Digital-7Mono, Digital-7 Mono" font-size="6" font-weight="normal" fill="#FFFFFF">
<tspan x="7" y="12">3</tspan>
</text>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<!-- 滑块验证组件 -->
<div ref='wrapper' class='wrapper' :style='styleObject'>
<!--<div class='refresh' @click='refreshPuzzle'>-->
<!--<span class='iconfont icon-refresh'></span>-->
<!--</div>-->
<div class='puzzle-wrapper' :width='width' :height='height'>
<img class='source' :src='bgImg' width='100%' :height='height' />
<img
id='puzzle-img'
class='dest'
:src='puzzleImg'
:width='sideLength'
:height='sideLength'
/>
</div>
<div class='button' :class='buttonClass' :style="'width:' + width">
<div
v-show='currentMatch !== 1'
class='slide'
:style="'left:' + this.left + 'px'"
@mousedown='startDrag'
>
<i class='el-icon-d-arrow-right'></i>
</div>
<div
v-if='/iPad|iPhone|Android/.test(oS)'
v-show='currentMatch !== 1'
class='slide'
:style="'left:' + this.left + 'px'"
@touchstart='readyMove'
>
<i class='el-icon-d-arrow-right'></i>
</div>
<div
v-else
v-show='currentMatch !== 1'
class='slide'
:style="'left:' + this.left + 'px'"
@mousedown='startDrag'
>
<i class='el-icon-d-arrow-right'></i>
</div>
<span class='text' v-if='this.currentMatch === 0'
>向右滑动滑块填充拼图</span
>
<span class='text' v-if='this.currentMatch === 1'>验证通过</span>
<span class='text' v-if='this.currentMatch === 2'
>向右滑动滑块填充拼图</span
>
</div>
<hr class='wrapper-footer-hr' />
<div class='wrapper-footer'>
<i class='el-icon-close' @click='close'></i>
<i class='el-icon-refresh' @click='refreshPuzzle'></i>
<!--<div title='刷新验证'>
<svg
style='cursor: pointer'
@click='refreshPuzzle'
class='iconCode'
aria-hidden='true'
>
<use xlink:href='#icon-shuaxin'></use>
</svg>
</div>
<div title='关闭验证'>
<svg
style='cursor: pointer'
@click='close'
class='iconCode'
aria-hidden='true'
>
<use xlink:href='#icon-xianshi_quxiao'></use>
</svg>
</div>-->
</div>
</div>
</template>
<script>
// import { captchaY } from '@/api//api/common.js'
import { baseUrl } from '../config'
export default {
name: 'puzzle-validator',
props: {
width: {
type: Number,
default: 380
},
height: {
type: Number,
default: 190
},
isMatch: {
type: Number
}
},
watch: {
isMatch: function(val) {
this.index = val
},
currentMatch: function(val) {
if (val === 0) {
this.buttonClass = 'info'
}
if (val === 1) {
this.buttonClass = 'success'
this.$emit('onlogin')
}
if (val === 2) {
// this.refreshPuzzle()
document.getElementById('puzzle-img') && (document.getElementById('puzzle-img').style.left = '0px')
// this.buttonClass = 'danger'
this.left = 0
}
}
},
computed: {
currentMatch: {
get() {
// 回调函数 当需要读取当前属性值是执行,根据相关数据计算并返回当前属性的值
return this.index
},
set(val) {
// 监视当前属性值的变化,当属性值发生变化时执行,更新相关的属性数据
this.index = val
}
}
},
mounted() {
this.refreshPuzzle()
// this.getCaptcha()
this.oS = this.detectOS()
},
data() {
return {
oS: '',
index: this.isMatch,
// 正方形左上角坐标
horizontal: 0,
vertical: 0,
left: '0px',
disX: 0,
isDrag: false,
buttonClass: 'info',
styleObject: {
height: this.height + 60 + 59 + 'px',
width: this.width + 'px'
},
codeTag: '',
bgImg: '',
puzzleImg: '',
sideLength: '76px'
}
},
methods: {
close() {
this.$emit('close')
},
detectOS() {
return navigator.userAgent
},
refreshPuzzle() {
this.buttonClass = 'info'
// this.currentMatch=0
this.index = 0
this.left = 0
document.getElementById('puzzle-img').style.left = '0px'
this.getCaptcha()
},
async getCaptcha() {
this.getCodeTag()
await this.getX()
await this.getBackgroundImg()
await this.getPuzzleImg()
},
getCodeTag() {
this.codeTag = new Date().getTime()
},
async getX() {
const data = {
code_tag: this.codeTag
}
// TODO 获取图形拖拽验证码
// const res = await captchaY(data)
// if (res) {
// this.vertical = res.y + '%'
// document.getElementById('puzzle-img').style.top = this.vertical
// document.getElementById('puzzle-img').style.marginTop = '-40px'
// }
},
async getBackgroundImg() {
this.bgImg = `${
baseUrl
}/backend/account/captcha_img/?code_tag=${this.codeTag}&type=${1}`
},
async getPuzzleImg() {
this.puzzleImg = `${
baseUrl
}/backend/account/captcha_img/?code_tag=${this.codeTag}&type=${2}`
},
readyMove(e) {
this.isDrag = true
const odiv = e.changedTouches[0].pageX
// 算出鼠标相对元素的位置
this.disX = odiv
document.ontouchmove = em => {
this.mobileMove(em)
}
document.ontouchend = () => {
this.endDrag()
}
},
mobileMove(e) {
if (this.isDrag) {
const ml = e.changedTouches[0].pageX - this.disX
if (ml < 0) {
this.left = 0
return
}
if (ml > 0) {
this.left = e.changedTouches[0].pageX - this.disX
document.getElementById('puzzle-img').style.left = this.left + 'px'
}
}
},
endDrag() {
document.ontouchend = null
document.ontouchmove = null
this.isDrag = false
const percent = (this.left + 40) / this.width
const obj = {
code_tag: this.codeTag,
x_percent: percent.toFixed(4) * 100
}
this.$emit('getMatchInfo', obj)
},
startDrag(e) {
this.isDrag = true
const odiv = e.target
// 算出鼠标相对元素的位置
this.disX = e.clientX - odiv.offsetLeft
document.onmousemove = em => {
this.move(em)
}
document.onmouseup = () => {
this.stopDrag()
}
},
move(e) {
if (this.isDrag) {
const ml = e.clientX - this.disX
if (ml < 0) {
this.left = 0
return
}
if (ml > this.width - 40) {
this.left = this.width - 40
return
}
this.left = e.clientX - this.disX
document.getElementById('puzzle-img').style.left = this.left + 'px'
}
},
stopDrag() {
document.onmouseup = null
document.onmousemove = null
this.isDrag = false
const percent = (this.left + 40) / this.width
const obj = {
code_tag: this.codeTag,
x_percent: percent.toFixed(4) * 100
}
this.$emit('getMatchInfo', obj)
}
}
}
</script>
<style scoped>
.wrapper {
position: relative
}
.wrapper-footer {
height: 35px;
line-height: 35px;
vertical-align: middle;
color: #aaa;
font-size: 20px;
font-weight: bold
}
.wrapper-footer i {
cursor: pointer
}
.wrapper-footer-hr {
border: none;
border-top: 1px solid #eee;
width: calc(100% + 20px);
margin-left: -10px
}
.refresh {
position: absolute;
top: 5px;
right: 5px
}
.puzzle-wrapper {
position: relative
}
.dest {
position: absolute;
top: 0;
left: 0
}
.text {
user-select: none
}
.button {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 40px;
margin-top: 10px;
border: 1px solid rgb(228, 231, 235)
}
.info {
color: #949494;
background: #dfe1e2
}
.success {
color: #fff;
background: rgb(103, 194, 58)
}
.danger {
color: #fff;
background: rgb(245, 108, 108)
}
.slide {
position: absolute;
top: 0px;
left: 0px;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
margin-right: 15px;
background: #ffffff;
box-sizing: border-box;
color: #949494;
border: 1px solid rgb(203, 205, 209);
margin-bottom: 20px
}
</style>
<!-- 日期组件
反给父及组件选中的日期 格式为 'YYYY-MM-DD'
抛出的方法为 this.$emit('update-date', this.currentChooseTime)
-->
<template lang="pug">
.calendar-area
.choose-box.flex.justify
dropdown.flex-1(
placement="bottom-start",
offset="-2,7",
:is-list="false",
style="display: block",
)
input(
type="text",
:placeholder="$t('container.pleaseChoose')",
v-model="currentChooseTime",
@focus="showCalendar = true",
@change="changeDate",
:is-list="false",
slot="dropdown-link",
)
.datepicker-popup(
v-if="showCalendar",
slot="dropdown-list",
)
.datepicker-popup-header.flex.justify.center
.iconfont.icon-triangle-left.pointer(
@click.stop="currentMonth = currentMonth - 1"
)
.current-date
span(style="margin-right: 10px") {{ currentYear }}
span {{ currentMonth }}
.iconfont.icon-triangle-right.pointer(
@click.stop="currentMonth = currentMonth + 1"
)
.weekday
span.day-tips(v-for="week in weeks") {{ week }}
.days
span.day.last-month(
v-for="day in days.lastMonthDays",
) {{ day }}
span.day(
:class="{active: currentDay === day,'current-month': judgeIsActiveDays(day)}"
v-for="day in days.totalDay",
@click="chooseDate(day)"
) {{ day}}
span.day.next-month(
v-for="day in days.nextMonthDay",
) {{ day }}
.iconfont.icon-calendar
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class calendar extends Vue {
@Prop({ type: Number, default: 0 }) startMonth
@Prop({ type: Number, default: 0 }) startDay
showCalendar: boolean = false
currentChooseTime: string = ''
weeks: Array<string> = ['一', '二', '三', '四', '五', '六', '日']
currentMonth: number = new Date().getMonth() + 1
currentYear: number = new Date().getFullYear()
currentDay: number = new Date().getDate()
// 计算当月的天数,以及空缺位置的上月和下月日期
get days() {
const year = this.currentYear
const month = this.currentMonth
// 获取当前月份有多少天
const date = new Date(year, month, 0)
const totalDay = date.getDate()
// 获取上个月有多少天
const lastMonth = new Date(year, month - 1, 0)
const lastMonthDaysNum: number = lastMonth.getDate()
// 获取当前月份1号是星期几
const firstDay = new Date(year, month, 1)
const startDay = new Date(year + '/' + month + '/' + '01').getDay()
const placeholder = startDay === 0 ? 6 : startDay - 1
const lastMonthDays: Array<number> = []
for (let i = 0; i < placeholder; i++) {
const data: number = lastMonthDaysNum - i
lastMonthDays.unshift(data)
}
const tempSum = placeholder + totalDay
const nextMonthDay = tempSum % 7 === 0 ? 0 : 7 - (tempSum % 7)
return { totalDay, lastMonthDays, nextMonthDay }
}
get showMonth() {
return this.currentMonth < 10 ? `0${this.currentMonth}` : this.currentMonth
}
get showDay() {
return this.currentDay < 10 ? `0${this.currentDay}` : this.currentDay
}
// 判断是否是可选的日期
judgeIsActiveDays(day) {
const data = (this.startMonth < this.currentMonth ||
(this.startMonth === this.currentMonth && this.startDay <= day))
return data
}
chooseDate(day) {
const isActiveDays = this.judgeIsActiveDays(day)
if (!isActiveDays) return
this.currentDay = day
this.currentChooseTime = `${this.currentYear}-${this.showMonth}-${this.showDay}`
const params = {
year: this.currentYear,
month: this.currentMonth,
date: this.currentDay,
}
this.$emit('update-date', params)
// this.$emit('update-date', this.currentChooseTime)
}
changeDate() {
this.currentChooseTime = ''
this.$emit('update-date', this.currentChooseTime)
}
}
</script>
<style lang="sass" scoped>
.choose-box
background: #FFFFFF
border: 1px solid #D7DDE4
border-radius: 2px
width: 200px
height: 36px
padding-right: 10px
input
border: none
color: #666
.icon-calendar
// margin: 0 10px
color: #9DA7B4
.dropdown
.datepicker-popup
width: 312px
background: #FFFFFF
// border: 1px solid #D7DDE4
border-radius: 2px
.datepicker-popup-header
width: 100%
height: 50px
padding: 0 33px
border-bottom: 1px solid #E3E8EE
.iconfont
color: #9da7b4
.weekday
// border-bottom: 1px solid #E3E8EE
padding: 0 15px
.day-tips
display: inline-block
width: 40px
height: 37px
line-height: 37px
text-align: center
.days
width: 100%
padding: 0 15px 10px 15px
.day
display: inline-block
cursor: pointer
width: 40px
height: 40px
line-height: 40px
text-align: center
color: #C3CBD6
.current-month
color: #333
cursor: pointer
&:hover, &.active
background: #409EFF
border-radius: 50%
color: #FFF
.last-month, .next-month
color: #C3CBD6
</style>
<!-- 日期组件
接收参数isTimeFrame 是否显示为时间区间
isTimeFrame 若为false
反给父及组件选中的日期 格式为 'YYYY-MM-DD'
isTimeFrame 若为true
反给父级object { from: 'YYYY-MM-DD', to: 'YYYY-MM-DD'}
抛出的方法为 this.$emit('update-date', this.currentChooseTime)
-->
<template lang="pug">
.date-picker.flex.left
calendar(
@update-date="getStartDate"
)
.timeFrame.flex.left(v-if="isTimeFrame")
.interval 至
calendar(
:start-month="startMonth",
:start-day="startDate",
@update-date="getEndDate",
)
</template>
<script lang="ts">
import calendar from './_calendar.vue'
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component({
components: { calendar },
})
export default class datePicker extends Vue {
@Prop({ type: Boolean, default: true }) isTimeFrame
startYear: number = new Date().getFullYear()
startMonth: number = new Date().getMonth() + 1
startDate: number = new Date().getDate()
endYear: number = new Date().getFullYear()
endMonth: number = new Date().getMonth() + 1
endDate: number = new Date().getDate()
get showStartDate() {
const month = this.getShowDate(this.startMonth)
const day = this.getShowDate(this.startDate)
return `${this.startYear}-${month}-${day}`
}
get showEndDate() {
const month = this.getShowDate(this.endMonth)
const day = this.getShowDate(this.endDate)
return `${this.endYear}-${month}-${day}`
}
getShowDate(Date) {
return Date < 10 ? `0${Date}` : Date
}
getStartDate(val) {
if (!val) {
this.$emit('update-date', val)
return
}
this.startYear = val.year
this.startMonth = val.month
this.startDate = val.date
if (!this.isTimeFrame) {
this.$emit('update-date', this.showStartDate)
}
// else {
// this.$emit('update-date', `${this.showStartDate}_${this.showEndDate}`)
// }
}
getEndDate(val) {
if (!val) {
this.$emit('update-date', val)
return
}
this.endYear = val.year
this.endMonth = val.month
this.endDate = val.date
this.$emit('update-date', `${this.showStartDate}_${this.showEndDate}`)
}
}
</script>
<style lang="sass" scoped>
.date-picker
.interval
margin: 0 20px
color: #666
</style>
<!-- yaml编辑组件 -->
<template lang="pug">
codemirror.mirror-content(
ref="codeMirror",
:value="value",
:options="editorOptions",
@input="inputValue",
)
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
import { codemirror } from 'vue-codemirror'
import jsonYaml from 'js-yaml'
import 'codemirror/lib/codemirror.css'
import 'codemirror/mode/yaml/yaml.js'
@Component({
components: { codemirror },
})
export default class Code extends Vue {
@Prop({ default: null }) yaml
value: string = ''
get editorOptions() {
return {
tabSize: 2,
mode: 'text/x-yaml',
// lineWrapping: true,
}
}
get codemirror() {
return (this.$refs.codeMirror as any).codemirror
}
@Watch('yaml', { immediate: true })
init(val) {
try {
this.value = val ? jsonYaml.safeDump(val) : ''
} catch (e) {
this.$notify.error({
title: 'yaml格式错误',
message: e.message,
})
}
setTimeout(() => {
this.codemirror.setSize('100%', '100%')
})
}
inputValue(val) {
// 该事件暂时只需要获知输入内容,不需要转换
this.$emit('change', val)
}
submit() {
const value = this.codemirror.getValue()
try {
const jsonResult = jsonYaml.safeLoad(value)
return jsonResult
} catch (err) {
Toast.danger(vm.$t('public.yamlFormatError'))
}
}
}
</script>
<style lang="sass" scoped>
.mirror-content
overflow-y: auto
</style>
<style lang="sass">
.mirror-content
.CodeMirror
padding: 10px 15px
background: inherit
</style>
import Http from '@/http'
import { baseUrl } from '@/config'
import imgPretreat from '@/helpers/img-pretreat'
interface trData {
index: number,
data: { [key:string]: { index: number, type: string, value: any, originType: string } },
}
interface tdData {
index: number, type: string, value: any, originType: string
}
function base64toText(base64, type): Promise<any> {
// 将base64转为Unicode规则编码
const bstr = window.atob(base64)
let n = bstr.length
const u8arr = new Uint8Array(n)
let blobDate: any = {}
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
blobDate = new Blob([u8arr], { type })
// return this.blobToText(blobDate, type)
return new Promise((resolve, reject) => {
const fileReader = new FileReader()
const codeStyle = type.split('/')[1] === 'csv'
? 'GB18030'
: 'utf-8'
fileReader.readAsText(blobDate, codeStyle)
fileReader.onload = e => {
resolve(fileReader.result!)
}
})
}
export function formatDataList(propertyList): trData[] {
function classifyResource(contentType): string {
if (/^image/.test(contentType)) return 'image'
if (/^audio/.test(contentType)) return 'audio'
if (/^video/.test(contentType)) return 'video'
if (/^(text|application\/xml)/.test(contentType)) return 'text'
return 'unknow'
}
return _.map(propertyList, item => {
return {
index: item.index,
// type: classifyResource(item.format.contentType),
// data: item.data,
// conver: '',
data: _.reduce(item.data, (result, value, key) => {
const type = item.format[key] ? classifyResource(item.format[key]) : ''
result[key] = { index: item.index, type, value, originType: item.format[key] }
return result
}, {}),
}
})
}
export async function getConverData(rawDatasetId: number, dataList: trData[]) {
async function getImageData(dataUrl: string, tdData: tdData) {
const imgEle = await imgPretreat(dataUrl)
tdData.value = imgEle || ''
}
async function getMediaData(dataUrl: string, tdData: tdData) {
tdData.value = dataUrl
}
async function getTextData(dataUrl: string, tdData: tdData) {
const textData = await Http.get(dataUrl, {}, { routerCancel: true, valid_code: false })
tdData.value = textData
}
const requestList: Promise<void>[] = []
_.forEach(dataList, trData => {
_.forEach(trData.data, (tdData, tdIndex) => {
const dataUrl = `${baseUrl}/kplrecord/${rawDatasetId}/${trData.index}?key=${tdIndex}`
if (tdData.type === 'image') requestList.push(getImageData(dataUrl, tdData))
if (tdData.type === 'audio') requestList.push(getMediaData(dataUrl, tdData))
if (tdData.type === 'video') requestList.push(getMediaData(dataUrl, tdData))
if (tdData.type === 'text') requestList.push(getTextData(dataUrl, tdData))
})
})
await Promise.all(requestList)
}
<!-- 数据属性弹窗 -->
<template lang="pug">
popup.familiar(
:show.sync="showPopup",
popup-style="width: 500px; height: 400px",
fixed-size,
@dismiss="",
)
.popup-header(slot="header")
span {{$t('public.dataAttr')}}
.popup-body(slot="body")
.content {{ value }}
.popup-footer.flex.right(slot="footer")
.buttons.flex
button.btn.btn-gray(@click="showPopup = false") {{$t('public.cancel')}}
button.btn.btn-important(@click="confirm") {{$t('pipeline.copy')}}
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
import { copyText } from '@/utils/dom'
@Component
export default class datasetKeyPopup extends Vue {
@Prop({ type: Boolean, default: false }) show
@Prop({ default: '' }) value
get showPopup() { return this.show }
set showPopup(val) { this.$emit('update:show', val) }
// 确认赋值
confirm() {
const type = typeof this.value
copyText(type === 'object' ? JSON.stringify(this.value) : this.value)
this.showPopup = false
}
}
</script>
<style lang="sass" scoped>
.popup-body
padding: 30px
overflow: hidden
.content
line-height: 20px
height: 100%
overflow-y: auto
white-space: pre-wrap
</style>
<template lang="pug">
.data-td(v-if="display")
.image.flex.center(
v-if="display.type === 'image'",
@click="$emit('show-image', dataItem.index, keyItem.key)",
:class="changeLang?'dataset-detailJa':usedBy",
)
template(v-if="display.value")
img(:src="display.value.src")
.audio.flex.left(
v-if="display.type === 'audio'",
:class="changeLang?'audioJa':usedBy",
)
template(v-if="display.value")
.placeholder-icon(
v-if="!isPlaying"
@click="isPlaying = true",
)
img(src="@/assets/file-icon/audio.png")
audio(
v-else,
controls,
autoplay="autoplay",
controlsList="nodownload",
)
source(
:type="display.originType",
:src="display.value",
)
.video.flex.left(
v-if="display.type === 'video'",
:class="changeLang?'videoJa':usedBy",
)
template(v-if="display.value")
.placeholder-icon(
@click="isPlaying = true",
)
img(src="@/assets/file-icon/video.png")
video-preview(
:show.sync="isPlaying",
:data="display",
)
.text.pointer(
ref="textWrap",
v-if="display.type === 'text'",
v-tooltip-on-clip="tooltipOption",
@click="showText",
)
span {{ display.text | placeholder }}
.unknow(v-if="display.type === 'unknow'")
span {{$t('public.noKnowType')}}
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component({
components: {
videoPreview: () => import(/* webpackChunkName: "dataset" */ './_video-preview.vue'),
},
})
export default class dataTd extends Vue {
$refs!: {
textWrap: HTMLLabelElement,
}
// 当前 行数据
@Prop({ type: Object, default: null }) dataItem
// 当前 列数据
@Prop({ type: Object, default: null }) keyItem
// 被使用于 默认使用于数据详情页的数据属性 另外一种使用于工作空间数据详情侧边栏
// dataset-detail | workspace
@Prop({ type: String, default: 'dataset-detail' }) usedBy
isPlaying: boolean = false
changeLang:boolean = false
// get contentType() { return this.dataItem.data.contentType || '' }
get tooltipOption() {
return {
value: this.display && this.display.value,
appendTo: this.usedBy === 'workspace' ? document.body : 'parent',
}
}
get display() {
const tdData = _.get(this.dataItem.data, [this.keyItem.key])
if (['ByteArray', 'File'].includes(this.keyItem.type)) {
let text = ''
if (tdData.type === 'text') {
const temp = typeof tdData.value === 'object' ? JSON.stringify(tdData.value) : tdData.value
text = _.truncate(temp, { length: 200 })
}
return {
...tdData,
text,
}
}
const text = typeof tdData.value === 'object' ? JSON.stringify(tdData.value) : tdData.value
const result: { type: string, value: string, text: string } = {
...tdData,
type: 'text',
text: _.truncate(text, { length: 200 }),
}
return result
}
showText(event) {
const tirggerEle = this.$refs.textWrap
if (!tirggerEle.contains(event.target)) return
if (tirggerEle.clientWidth >= tirggerEle.scrollWidth) return
this.$emit('show-text', this.display!.value)
}
@Watch('$route', { immediate: true })
changeLanguage() {
if (this.$route.params.lang === 'zh-cn') {
this.changeLang = false
} else {
this.changeLang = true
}
}
}
</script>
<style lang="sass" scoped>
.data-td
img
max-height: 100%
max-width: 100%
.text
white-space: nowrap
overflow: hidden
&[tabindex]
text-overflow: ellipsis
&:hover
text-decoration: underline
color: #378DFF
.unknow
color: #CCC
white-space: nowrap
overflow: hidden
.image
width: 40px
&.workspace
height: 40px
background: #D8D8D8
&.dataset-detail
height: 30px
background: #D8D8D8
cursor: pointer
position: relative
&::after
display: none
position: absolute
content: '查看'
top: 0
left: 0
width: 100%
height: 100%
color: #FFF
line-height: 30px
text-align: center
&:hover::after
display: block
background: rgba(0,0,0,0.5)
&.dataset-detailJa
height: 30px
background: #D8D8D8
cursor: pointer
position: relative
&::after
display: none
position: absolute
content: '詳細'
top: 0
left: 0
width: 100%
height: 100%
color: #FFF
line-height: 30px
text-align: center
&:hover::after
display: block
background: rgba(0,0,0,0.5)
.audio, .video
.placeholder-icon
width: 30px
height: 30px
cursor: pointer
position: relative
text-align: center
&::after
display: none
position: absolute
content: '播放'
top: 0
left: 0
width: 100%
height: 100%
color: #FFF
line-height: 30px
&:hover::after
display: block
background: rgba(0,0,0,0.5)
.audioJa, .videoJa
.placeholder-icon
width: 30px
height: 30px
cursor: pointer
position: relative
text-align: center
&::after
display: none
position: absolute
content: '放送'
top: 0
left: 0
width: 100%
height: 100%
color: #FFF
line-height: 30px
&:hover::after
display: block
background: rgba(0,0,0,0.5)
.audio
// 该宽度是火狐显示音量调节的最小宽度
width: 270px
&.workspace
height: 40px
&.dataset-detail
height: 30px
audio
width: 100%
height: 30px
</style>
<!-- 视频播放 -->
<template lang="pug">
.video-player(v-if="propShow")
.mask
.video-content.flex.center(@click.stop="propShow = false")
video(
controls,
autoplay="autoplay",
controlsList="nodownload",
width="800",
height="600",
@click.stop="",
)
source(
:type="data.originType",
:src="data.value",
)
p Your browser doesn't support HTML5 video.
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class VideoPreview extends Vue {
@Prop({ type: Object, required: true }) data
@Prop({ type: Boolean, default: false }) show
get propShow() { return this.show }
set propShow(val) { this.$emit('update:show', val) }
}
</script>
<style lang="sass" scoped>
.video-player
.mask
position: fixed
z-index: 901
left: 0
right: 0
top: 0
bottom: 0
background-color: rgba(0,0,0,.8)
.video-content
padding: 0
position: fixed
top: 0
left: 0
right: 0
bottom: 0
z-index: 902
</style>
interface markItem {
name: string,
xmin: number,
xmax: number,
ymin: number,
ymax: number,
}
export function getClipArray(imgEle: HTMLImageElement, currentMarkInfo: markItem[]) {
const canvas = document.createElement('canvas')
canvas.width = imgEle.width
canvas.height = imgEle.height
const context = canvas.getContext('2d')
context!.drawImage(imgEle, 0, 0)
if (!_.isArray(currentMarkInfo)) return []
const result = _.map(currentMarkInfo, clipItem => {
if (typeof clipItem.xmax !== 'number') return null
if (clipItem.xmin >= clipItem.xmax || clipItem.ymin >= clipItem.ymax) return null
const clipImage: ImageData = context!.getImageData(
clipItem.xmin,
clipItem.ymin,
clipItem.xmax - clipItem.xmin,
clipItem.ymax - clipItem.ymin,
)
return {
name: clipItem.name,
content: createNewCanvas(clipImage),
}
})
return <{ name: string, content: string }[]>_.without(result, null)
}
// 将canvas转换为base64
function createNewCanvas(clipImage: ImageData) {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
canvas.width = clipImage.width
canvas.height = clipImage.height
context!.putImageData(clipImage, 0, 0)
return canvas.toDataURL()
}
'use strict'
function Point(x:number, y:number): { x:number, y:number } {
return {
x: x,
y: y,
}
}
// function calcNewPoint(point: any, pCenter: any, angle: any): any {
// const newAngle = (angle * Math.PI) / 180
// const cosv = Math.cos(newAngle)
// const sinv = Math.sin(newAngle)
// const newX = (point.x - pCenter.x) * cosv - (point.y - pCenter.y) * sinv + pCenter.x
// const newY = (point.x - pCenter.x) * sinv + (point.y - pCenter.y) * cosv + pCenter.y
// return Point(newX, newY)
// }
// 获取rotbox四个顶点坐标
// function getPointCoordinates(width, height, x, y, angle) {
// const halfHeight = height / 2.0
// const halfWidth = width / 2.0
// const pCenter = Point(x, y)
// // 获取左上角坐标 中心坐标 x 减去宽的一半,中心坐标 y 加上高的一半
// const topLeft = Point(x - halfWidth, y + halfHeight)
// // 右上角坐标 中心坐标 x 加宽的一半 中心坐标y 加高的一半
// const topRight = Point(x + halfWidth, y + halfHeight)
// // 左下角坐标 中心坐标 x 减宽的一半 中心坐标y 减去 高的一半
// const bottomLeft = Point(x - halfWidth, y - halfHeight)
// // 右上角坐标 中心坐标 x 加宽的一半 中心坐标y 减去 高的一半
// const bottomRight = Point(x + halfWidth, y - halfHeight)
// const pointArr: { x: number, y: number }[] = []
// pointArr.push(calcNewPoint(topLeft, pCenter, -angle))
// pointArr.push(calcNewPoint(topRight, pCenter, -angle))
// pointArr.push(calcNewPoint(bottomRight, pCenter, -angle))
// pointArr.push(calcNewPoint(bottomLeft, pCenter, -angle))
// return pointArr
// }
// 绘制多边形框
function drawRectangle(ctx, pointArr, data, isAddText) {
pointArr.forEach((pointItem, index) => {
if (index === 0) {
ctx.strokeStyle = data.lineColor
ctx.beginPath()
ctx.moveTo(pointItem.x, pointItem.y)
return
}
ctx.lineTo(pointItem.x, pointItem.y)
if (index === pointArr.length - 1) {
ctx.closePath()
ctx.font = '18px Arial'
ctx.lineWidth = '3'
ctx.fillStyle = data.lineColor
ctx.globalAlpha = data.isContain ? 1 : 0.5
const name = data.display_name || data.name
if (name && isAddText) {
// 获取文字的高
// const height = ctx.measureText(data.name).actualBoundingBoxAscent * 2 + 2
// 背景矩形的高
const height = 23
const isBeyondEdges = pointArr[3].y <= height
const rectX = isBeyondEdges ? pointArr[3].x : pointArr[3].x - 1.5
const rectY = isBeyondEdges ? pointArr[3].y : pointArr[3].y - height
ctx.fillRect(rectX, rectY, ctx.measureText(data.name).width + 6, height)
ctx.textAlign = 'top'
ctx.fillStyle = '#fff'
ctx.shadowBlur = 1
ctx.shadowColor = 'rgba(0,0,0,0.5)'
const namaY = isBeyondEdges ? pointArr[3].y + 16 : pointArr[3].y - 6
ctx.fillText(name, pointArr[3].x + 2, namaY)
}
ctx.stroke()
}
})
}
// 获取bndbox 四个点坐标
function getBndboxCoordinates(data, zoomRate) {
const xmin = data.xmin * zoomRate
const xmax = data.xmax * zoomRate
const ymin = data.ymin * zoomRate
const ymax = data.ymax * zoomRate
const pointA = Point(xmin, ymax)
const pointB = Point(xmax, ymax)
const pointC = Point(xmax, ymin)
const pointD = Point(xmin, ymin)
const pointArr: { x:number, y:number }[] = []
pointArr.push(pointA, pointB, pointC, pointD)
return pointArr
}
// 绘制斜框检测图的方向线
// function drawDirectionLine(ctx, data, zoomRate, rotate) {
// const centerLine = +data.rotbox.w > +data.rotbox.h
// ? data.rotbox.w / 2 * zoomRate
// : data.rotbox.h / 2 * zoomRate
// const x = Math.cos(Math.PI / 180 * rotate) * centerLine + (data.rotbox.x * zoomRate)
// const y = Math.sin(Math.PI / 180 * rotate) * centerLine + (data.rotbox.y * zoomRate)
// ctx.beginPath()
// ctx.moveTo(data.rotbox.x * zoomRate, data.rotbox.y * zoomRate)
// ctx.lineTo(x, y)
// ctx.stroke()
// }
// 绘制斜框检测图
// function drawSkewFrameDetection(ctx, data, zoomRate, location, allLabelInfo) {
// const width = +data.rotbox.w * zoomRate
// const height = +data.rotbox.h * zoomRate
// const xCenter = +data.rotbox.x * zoomRate
// const yCenter = +data.rotbox.y * zoomRate
// const pointArr = getPointCoordinates(width, height, xCenter, yCenter, +data.rotbox.a)
// drawRectangle(ctx, pointArr, data, location, allLabelInfo)
// const rotate = -data.rotbox.a
// drawDirectionLine(ctx, data, zoomRate, rotate)
// }
// 绘制四边形
export function drawBndBox(ctx, data, zoomRate, isAddText: boolean = true) {
const bndArr = getBndboxCoordinates(data, zoomRate)
drawRectangle(ctx, bndArr, data, isAddText)
}
// 获取当前标注框的displayname
// function getDisplayName(data, allLabelInfo) {
// const currentLabel = data.display_name || data.name
// if (!allLabelInfo.currentLabels.includes(currentLabel)) {
// allLabelInfo.currentLabels.push(currentLabel)
// }
// }
// 根据不同的类别绘制标注框
// export function drawCanvasBox(ctx, data, zoomRate, location, allLabelInfo) {
// if (data.bndbox) {
// drawBndBox(ctx, data, zoomRate, location, allLabelInfo)
// } else if (data.rotbox) {
// drawSkewFrameDetection(ctx, data, zoomRate, location, allLabelInfo)
// }
// }
<!-- 算法/数据集/模型/处理器 编辑弹窗 -->
<template lang="pug">
popup.familiar(
:show.sync="propShow",
popup-style="width: 500px",
:percent-height=.8
@dismiss="setPropShow()",
)
.popup-header(slot="header")
.title {{this.$t('public.copy')}}
.popup-body(slot="body")
.top-tips {{$t('copy.fontAutoAdd')}}{{ typeText }}{{$t('copy.autoAddBack')}}
form-table(
ref="formTable"
:format-data="formatData",
:result-data="resultData",
:label-width="110",
style="width: 430px;margin-left: -4px",
)
.popup-footer(slot="footer")
.flex-1.flex.right
st-button(
@click="setPropShow()",
) {{$t('public.cancel')}}
st-button(
type="primary"
@click="confirm",
) {{$t('public.confirm')}}
</template>
<script lang="ts">
import { Vue, PropSync, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class dataEdit extends Vue {
$refs!: { formTable: any }
@PropSync('show', { type: Boolean, default: false }) propShow
@Prop({ type: String, default: '' }) type
@Prop({ type: Array, default: () => [] }) idList
resultData: { target_space_id: number } = {
target_space_id: 0,
}
spaceList: {}[] = []
get formatData(): {}[] {
return [
{
label: `${this.$t('copy.copyToSpace')}:`,
type: 'select',
key: 'target_space_id',
props: { options: this.$options.filters!.options(this.spaceList, 'id') },
validator: val => { if (!val) return vm.$t('model.chooseTarget') },
},
]
}
get typeText(): string {
switch (this.type) {
case 'dataset': return `${this.$t('navigation.dataSet')}`
case 'dataset_fs': return `${this.$t('navigation.dataSet')}`
case 'algo': return `${this.$t('index.algorithm')}`
case 'model': return `${this.$t('navigation.model')}`
case 'container': return `${this.$t('navigation.container')}`
default: return ''
}
}
setPropShow() {
this.propShow = false
if (this.type === 'dataset') {
this.$store.commit('uploadSerializedCopyPopup', false)
}
if (this.type === 'dataset_fs') {
this.$store.commit('uploadOriginCopyPopup', false)
}
}
async created() {
}
async confirm() {
const result = await this.$refs.formTable.submit()
const params = { ...result, id_list: this.idList }
if (!result) return
const requestMethod = (() => {
const params = { ...result, id_list: this.idList }
switch (this.type) {
// case 'dataset': return () => Dataset.copy(params)
// case 'dataset_fs': return () => Dataset.copyOrigin(params)
// case 'algo': return () => Algo.copy(params)
// case 'model': return () => Model.copy(params)
// case 'container': return () => Container.copy(params)
default: return () => {}
}
})()
const response = await requestMethod()
this.dealResponse(response)
}
dealResponse(res) {
if (res.code !== 'Success') return
Toast.success(this.$t('public.copyBackOpen', { type: this.typeText }))
this.propShow = false
if (this.type === 'container') {
// 如果是拷贝容器,显示弹出框提示
this.$emit('showMsg', res.data)
}
if (this.type === 'dataset') {
this.$store.commit('uploadSerializedCopyPopup', false)
}
if (this.type === 'dataset_fs') {
this.$store.commit('uploadOriginCopyPopup', false)
}
}
}
</script>
<style lang="sass" scoped>
.popup-body
padding: 10px 20px 0px 20px
.top-tips
font-size: 12px
margin: 10px 0
table.form
td:first-child
padding-top: 16px
input
width: 100%
</style>
<!-- 删除算法/数据集/模型/处理器 确认 -->
<template lang="pug">
popup.familiar(
:show.sync="propShow",
popup-style="width: 500px",
:percent-height=.8
@dismiss="propShow = false",
)
.popup-header(slot="header")
.title {{ typeText }}
.popup-body(slot="body")
.top-tips
.warning-tips {{ typeNoCover }}
.text-tips(v-if="detail")
span {{typePropName}}
span.algo-name {{ detail.name }}
span {{$t('public.confirmDeleteD')}}
.input-area
.key {{$t('public.confirmName')}}
input.input-text(
type="text",
:placeholder="typePlaceholder",
v-model="name",
@focus="showErrorTips = false",
)
.popup-footer.flex.justify(slot="footer")
.error-tips(
v-if="showErrorTips"
) {{ typeText }}{{$t('public.nameInputError')}}
.flex-1.flex.right
.btn.btn-gray(
@click="propShow = false",
) {{$t('public.cancel')}}
.btn.btn-important(
@click="confirm",
:disabledLabel="$t('public.handleing')"
) {{$t('public.confirm')}}
</template>
<script lang="ts">
import { Vue, PropSync, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class dataDelete extends Vue {
@PropSync('show', { type: Boolean, default: false }) propShow
@Prop({ type: Object, default: null }) detail
@Prop({ type: String, default: '' }) type
showErrorTips: boolean = false
name: string = ''
get typeText(): string {
switch (this.type) {
case 'dataset': return vm.$t('public.deleteDataset')
case 'dataset_fs': return vm.$t('public.deleteDataset')
case 'algo': return vm.$t('public.deleteAlgo')
case 'model': return vm.$t('public.deleteModel')
default: return ''
}
}
get typeNoCover(): string {
switch (this.type) {
case 'dataset': return vm.$t('public.dataSetNoCover')
case 'dataset_fs': return vm.$t('public.dataSetNoCover')
case 'algo': return vm.$t('public.algoNoCover')
case 'model': return vm.$t('public.modelNoCover')
default: return ''
}
}
get typePlaceholder(): string {
switch (this.type) {
case 'dataset': return vm.$t('public.inputDeleteDataset')
case 'dataset_fs': return vm.$t('public.inputDeleteDataset')
case 'algo': return vm.$t('public.inputDeleteAlgo')
case 'model': return vm.$t('public.inputDeleteModel')
default: return ''
}
}
get typePropName(): string {
switch (this.type) {
case 'dataset': return vm.$t('public.deleteDatasetName')
case 'dataset_fs': return vm.$t('public.deleteDatasetName')
case 'algo': return vm.$t('public.deleteAlgoName')
case 'model': return vm.$t('public.deleteModelName')
default: return ''
}
}
@Watch('propShow', { immediate: true })
resetForm(val) {
if (!val) return
this.name = ''
}
async confirm() {
if (this.name !== this.detail.name || !this.name) {
this.showErrorTips = true
return
}
this.showErrorTips = false
const requestMethod = (() => {
switch (this.type) {
// case 'dataset': return () => { return Dataset.delete({ dataset_id: this.detail.id }) }
// case 'dataset_fs': return () => { return Dataset.deleteOrigin({ dataset_id: this.detail.id }) }
// case 'algo': return () => { return Algo.delete({ algorithm_id: this.detail.id }) }
// case 'model': return () => { return Model.delete({ model_id: this.detail.id }) }
default: return () => {}
}
})()
const response = await requestMethod()
this.dealResponse(response)
// location.reload()
}
dealResponse(res) {
if (res.code === 'Success') {
Toast.success(this.$t('public.deleteSuc'))
this.propShow = false
routerRefresh()
} else {
Toast.danger(this.$t('public.deleteError'))
}
}
}
</script>
<style lang="sass" scoped>
.popup-header
// margin-bottom: 10px !important
.popup-body
// padding: 20px
.top-tips
.text-tips
margin-top: 5px
.algo-name
font-weight: 600
margin: 0 3px
color: #DC1818
.input-area
margin-top: 20px
.key
display: inline-block
font-size: 12px
.input-text
width: 300px
padding-left: 12px
&::-webkit-input-placeholder
color: #ccc
&:-moz-placeholder
color:#ccc
&::-moz-placeholder
color: #ccc
&:-ms-input-placeholder
color: #ccc
.popup-footer
.error-tips
color: #FA3C30
position: relative
margin-left: 10px
&:before
position: absolute
top: 3px
left: -10px
content: '*'
color: #FA3C30
</style>
<!-- 算法/数据集/模型/处理器 编辑弹窗 -->
<template lang="pug">
popup.familiar(
:show.sync="propShow",
popup-style="width: 520px",
:percent-height=.8
@dismiss="propShow = false",
)
.popup-header(slot="header")
.title {{$t('public.editor')}}{{ typeText }}
.popup-body(slot="body")
form-table(
ref="formTable"
:format-data="formatData",
:result-data="resultData",
:label-width="changeWidth",
:style="`width: 450px;margin-left: ${marginLeft}`",
)
.popup-footer(slot="footer")
.flex-1.flex.right
st-button(
@click="propShow = false",
) {{$t('public.cancel')}}
st-button(
type="primary"
@click="confirm",
) {{$t('public.confirm')}}
</template>
<script lang="ts">
import { Vue, PropSync, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class dataEdit extends Vue {
$refs!: { formTable: any }
@PropSync('show', { type: Boolean, default: false }) propShow
@Prop({ type: Object, default: () => {} }) detail
@Prop({ type: String, default: '' }) type
@Prop({ type: String, default: '-30px' }) marginLeft
resultData: { name: string, description: string } = {
name: '',
description: '',
}
changeWidth:number = 110
get typeText() {
switch (this.type) {
case 'dataset': return this.$t('navigation.dataSet')
case 'dataset_fs': return this.$t('navigation.dataSet')
case 'model': return this.$t('navigation.model')
case 'algo': return this.$t('index.algorithm')
}
}
get formatData(): {}[] {
return [
{
label: `${this.typeText}${this.$t('public.name')}:`,
type: 'input',
key: 'name',
validator: ['notEmpty', 'min2', 'max64', 'normName'],
props: { placeholder: this.$t('public.formatTwoSix') },
placeholder: this.$t('public.formatTwoSix'),
required: true,
}, {
label: `${this.typeText}${this.$t('public.desc')}:`,
type: 'textarea',
key: 'description',
},
]
}
@Watch('propShow', { immediate: true })
resetForm(val) {
if (!val) return
if (this.detail) {
this.resultData.name = this.detail.name
this.resultData.description = this.detail.description
} else {
this.resultData.name = ''
this.resultData.description = ''
}
}
checkExist(value):Promise<{
data: {existed: boolean}
}> {
const exist = false
// if (this.type === 'dataset') {
// return Dataset.checkName({ name: value })
// } else if (this.type === 'dataset_fs') {
// return Dataset.checkOriginName({ name: value })
// } else if (this.type === 'model') {
// return Model.checkExistName({ name: value })
// } else if (this.type === 'algo') {
// return Algo.checkExistedName({ name: value })
// }
return new Promise(() => {
return { data: { existed: false } }
})
}
async confirm() {
const result = await this.$refs.formTable.submit()
if (!result) return
const requestMethod = (() => {
switch (this.type) {
// case 'dataset': return () => Dataset.update({ ...result, dataset_id: this.detail.id })
// case 'dataset_fs': return () => Dataset.updateOrigin({ ...result, dataset_id: this.detail.id })
// case 'algo': return () => Algo.update({ ...result, algorithm_id: this.detail.id })
// case 'model': return () => Model.update({ ...result, model_id: this.detail.id })
default: return () => {}
}
})()
const response = await requestMethod()
this.dealResponse(response)
}
dealResponse(res) {
if (res.code === 'Success') {
Toast.success(this.$t('public.editSuc'))
this.propShow = false
routerRefresh()
} else {
Toast.danger(this.$t('public.editFail'))
}
}
@Watch('$route', { immediate: true })
changeLabelWidth() {
if (this.$route.params.lang === 'zh-cn') {
this.changeWidth = 110
} else {
this.changeWidth = 120
}
}
}
</script>
<style lang="sass" scoped>
.popup-body
padding: 10px 20px 0px 20px
table.form
td:first-child
padding-top: 16px
input
width: 100%
</style>
<template lang="pug">
.data-empty.flex.center
.tip-img.iconfont.icon-wushuju(
:style="tipIconStyle"
v-if="!isLoading"
)
.loading-gif(v-else)
.tip-text(:class="{ loading: isLoading }") {{tipDesc}}
</template>
<script>
export default {
name: 'data-empty',
props: {
isLoading: {
type: Boolean,
default: false,
},
tipText: {
type: String,
default: '没有找到相关数据',
},
tipIconStyle: {
type: String,
default: '',
},
},
computed: {
tipDesc() { return this.isLoading ? vm.$t('public.loadingCenter') + '...' : this.tipText },
},
}
</script>
<style lang="sass" scoped>
.data-empty
color: #666
width: 100%
padding-top: 60px
line-height: 1.5
.tip-img
font-size: 14px
.tip-text
margin-left: 10px
&.loading
color: #cccccc
.loading-gif
width: 14px
height: 14px
background: url(~@/assets/loading.gif) no-repeat center/100%
&.reverse
.tip-img
color: #F8B62D
.tip-text
color: #EEE
</style>
<template lang="pug">
seeta-tooltip(
v-if="showtooltip && item.type === 'td'"
placement="top"
)
template(
v-slot:content
)
span(v-html="tooltipValue")
td(
v-html="value",
)
td(
v-else-if="item.type === 'td'",
v-html="value",
)
td(
v-else-if="item.type === 'check'",
style="text-align: center",
@click.stop="checkRow",
)
input(
type="checkbox",
:checked="isCheck",
)
td(v-else-if="item.type === 'slot'")
slot
//- td(v-else)
</template>
<script>
import seetaTooltip from '@/components/seeta-ui/seeta-tooltip'
export default {
components: {
seetaTooltip,
},
props: {
// 这个 td 对应的 th item
item: Object,
// 这个 td 对应的 tr data
row: Object,
checkList: { type: Array, default: () => [] },
checkField: { type: String, default: 'id' },
},
computed: {
value() {
const display = _.isFunction(this.item.render)
? this.item.render(this.row)
: _.get(this.row, this.item.render)
return display == null || display === ''
? `<span style="color: #999">${this.item.placeholder}</span>`
: display
},
showtooltip() {
if (this.item.tooltip) {
return this.item.tooltip(this.row)
}
return false
},
tooltipValue() {
const display = _.isFunction(this.item.tooltipValue)
? this.item.tooltipValue(this.row)
: _.get(this.row, this.item.tooltipValue)
return display
},
// 用来判断是否匹配的对象
matchData() { return { [this.checkField]: this.row[this.checkField] } },
isCheck() {
if (this.item.type !== 'check') return false
return _.some(this.checkList, this.matchData)
},
},
methods: {
checkRow() {
if (this.isCheck) {
const deleteIndex = _.findIndex(this.checkList, this.matchData)
if (deleteIndex !== -1) this.checkList.splice(deleteIndex, 1)
} else {
this.checkList.push(this.row)
}
this.$emit('choose')
},
},
}
</script>
<style lang="sass" scoped>
</style>
<template lang="pug">
.main-progress
.text(
:style="{ color: color }"
)
span.left {{ text }}
span.right {{ currentRate + '%' }}
.progress-container
.progress-content(
:style="{background: outBackColor}",
)
.progress-bar(
:style="{background: backgroundColor, left: offsetWidth}",
)
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class ProgressBar extends Vue {
@Prop({ type: String, default: '' }) text
@Prop({ type: Number, default: 0 }) currentRate
@Prop({ type: String, default: '#409EFF' }) color
@Prop({ type: String, default: '#E8EBF2' }) outColor
get isOutOfMaxNumber():boolean {
return 100 - this.currentRate < 0
}
get offsetWidth():string | number {
const left = 100 - this.currentRate
return this.isOutOfMaxNumber ? 0 : `-${left}%`
}
get backgroundColor():string {
return this.isOutOfMaxNumber ? '#D66460' : this.color
}
get outBackColor():string {
return this.outColor
}
}
</script>
<style lang="sass" scoped>
.main-progress
display: inline-block
width: 100%
line-height: 19px
.progress-container
display: inline-block
width: 100%
height: 2px
position: relative
overflow: hidden
border-radius: 999px
margin-bottom: 3px
.progress-content
height: 100%
width: 100%
border-radius: 999px
.progress-bar
height: 100%
width: 100%
position: absolute
left: -100%
top: 0
border-radius: 999px
.left
display: inline-block
font-size: 13px
letter-spacing: 0
line-height: 13px
.right
display: inline-block
text-align: right
float: right
font-size: 13px
letter-spacing: 0
</style>
<!--dialog框下有一个选择按钮-->
<template lang="pug">
popup.confirm-popup(
:show.sync="show",
popup-style="max-width: 520px; width: 430px;",
:dismissable="false",
hide-header,
)
//- body
.confirm-body(slot="body")
//- span.dialog-icon.iconfont(:class="[mode, iconClass]")
.title
span.dialog-icon.iconfont(:class="[mode, iconClass]")
span.text {{ title }}
p.caption(v-if="message")
//- 支持html格式
span(v-html="message") {{ message }}
.options(v-if="options && options.length")
.option(v-for="option in options")
input.confirm-option(
type="checkbox",
v-model="selections",
:value="option"
)
span {{ option }}
//- footer
.confirm-footer.flex.right(slot="footer")
template(v-if="mode === 'confirm'")
button.btn.btn-primary(
@click="cancel",
style="margin-right: 20px"
) {{ cancelButtonText || buttonCancel }}
button.btn.btn-important(
@click="confirm",
ref="confirmBtn",
) {{ confirmButtonText || '确定' }}
template(v-if="mode === 'alertSelect'")
button.btn.btn-primary(
@click="cancel",
style="margin-right: 20px"
) {{ cancelButtonText || '取消' }}
button.btn.btn-important(
@click="confirm",
ref="confirmBtn",
) {{ confirmButtonText || '确定' }}
template(v-if="['warning', 'alert'].includes(mode)")
button.btn.btn-primary(
v-if="cancelButtonText"
@click="cancel",
style="margin-right: 20px"
) {{ cancelButtonText || '取消' }}
button.btn.btn-important(
@click="confirm",
ref="confirmBtn",
) {{ confirmButtonText || vm.$t('public.iKnow')}}
</template>
<script>
export default {
props: {
title: String,
message: String,
vm: Object,
/**
* confirm | alert | warning
*/
mode: {
type: String,
default: 'confirm',
},
/**
* 确认页面可以附带选项
*/
options: {
type: Array,
default() {
return []
},
},
confirmButtonText: {
type: String,
},
cancelButtonText: {
type: String,
},
},
data() {
return {
show: false,
selections: [],
buttonCancel: '',
buttonConfirm: '',
}
},
computed: {
iconClass() {
switch (this.mode) {
case 'confirm': return 'icon-jinggao'
case 'alert': return 'icon-tip'
case 'alertSelect': return 'icon-tip'
default: return 'icon-tip'
}
},
},
mounted() {
// NOTE 渲染完毕后设置 show = true, 以触发 transition 动画
this.show = true
this.buttonCancel = vm.$t('public.cancel')
this.buttonConfirm = vm.$t('public.confirm')
this.$nextTick(() => {
this.$refs.confirmBtn.focus()
})
},
methods: {
cancel() {
this.show = false
this.$emit('cancel')
},
confirm() {
this.show = false
if (this.options.length === 0) {
this.$emit('confirm', true)
}
// 构建一个记载选项是否被选择的boolean数组
const selectionResults = this.options.map(opt => _.includes(this.selections, opt))
this.$emit('confirm', selectionResults)
},
},
}
</script>
<style lang="sass" scoped>
@import "@/styles/theme.sass"
.confirm-body
padding: 40px
// text-align: center
.title
height: 22px
line-height: 22px
.dialog-icon
font-size: 20px
// vertical-align: top
&.confirm
color: $--color-primary
&.alert
color: #FF8805
&.warning
color: $--color-warning
&.alertSelect
color: #FF8805
.text
// text-align: center
font-size: 18px
height: 22px
line-height: 22px
font-weight: bold
color: $--color-text-primary
margin-left: 10px
.caption
color: #4C4C4C
font-size: 14px
margin-top: 20px
color: $--color-text-regular
.options
margin-top: 15px
font-size: 12px
.option+.option
margin-top: 5px
.confirm-option
vertical-align: middle
margin-right: 10px
.confirm-footer
padding: 16px 20px
border-top: 1px solid #E5E5E5
.btn
min-width: 56px
height: 32px
</style>
<template lang="pug">
.dropdown
.dropdown-link(
ref="trigger",
@click="toggleTrigger",
@mouseover="onMouseEnterTrigger",
@mouseleave="onMouseLeaveTrigger",
:class="{ show: show, disabled: disabled }",
)
slot(name="dropdown-link")
.dropdown-content(
ref="menu",
v-show="show",
:class="{list: isList, userInfo: userInfo, space: space}",
:style="{zIndex: zIndex}",
@click.stop="autoSetClose",
)
.dropdown-menu(
ref="dropdown",
@mouseenter="onMouseEnterDropdown",
@mouseleave="onMouseLeaveDropdown",
)
slot(name="dropdown-list")
</template>
<script>
import Popper from 'popper.js'
import { getElementProps } from '@/utils/dom'
export default {
name: 'dropdown',
data() {
return {
// timers used for hover mode
showTimer: null,
hideTimer: null,
popper: null,
hasCalculatedMenuWidth: false,
show: false,
}
},
props: {
// 空间下拉框top去掉间隙
space: {
type: Boolean,
default: false,
},
// 个人中心下拉框top去掉间隙
userInfo: {
type: Boolean,
default: false,
},
// 下拉内容的z-index属性值
zIndex: {
type: Number,
default: 1000,
},
triggerOnHover: {
type: Boolean,
default: false,
},
// 点击菜单后自动关闭
autoClose: {
type: Boolean,
default: true,
},
// 展开区域初始位置
placement: {
type: String,
default: 'bottom-start',
},
// 展开区域位置是否固定
positionFixed: {
type: Boolean,
default: false,
},
// 默认dropdown为列表样式(会对内部li元素写入样式)
isList: {
type: Boolean,
default: true,
},
// 用于调整下拉菜单的位置偏移
offset: {
type: String,
default: '5,3',
},
// 是否禁用当前 dropdown
disabled: {
type: Boolean,
default: false,
},
// 是否主动避免边缘
// avoidEdge: {
// type: Boolean,
// default: true,
// },
// 计算宽度
computedWidth: {
type: Boolean,
default: true,
},
},
mounted() {
document.addEventListener('click', this.domClose)
},
beforeDestroy() {
const menu = this.$refs.menu
if (menu && menu.parentNode === document.body) document.body.removeChild(menu)
document.removeEventListener('click', this.domClose)
},
watch: {
show(val, oldVal) {
if (val === oldVal) return
if (val) {
this.$emit('expand')
const trigger = this.$refs.trigger
const menu = this.$refs.menu
// so it's not constrained by any container
document.body.appendChild(menu)
this.$nextTick(() => {
// calculate menu width
if (this.computedWidth) {
if (!this.hasCalculatedMenuWidth && this.placement.includes('bottom')) {
const menuWidth = +getElementProps(menu, 'width')
const triggerWidth = +getElementProps(trigger, 'width')
menu.style.width = `${_.max([menuWidth, triggerWidth])}px`
this.hasCalculatedMenuWidth = true
}
}
this.popper = new Popper(trigger, menu, this.popperConfig)
})
} else {
this.$emit('on-close')
this.$emit('collapse')
if (this.popper) this.popper.destroy()
const menu = this.$refs.menu
if (menu && menu.parentNode === document.body) document.body.removeChild(menu)
}
},
},
computed: {
popperConfig() {
return {
placement: this.placement,
positionFixed: this.positionFixed,
modifiers: {
offset: { offset: this.offset },
hide: { enabled: false },
preventOverflow: { boundariesElement: 'window' },
},
}
},
},
methods: {
// 点击触发 展开关闭
toggleTrigger() {
if (this.triggerOnHover || this.disabled) return
this.show = !this.show
},
// 直接触发关闭
close() { this.show = false },
// 由dom点击触发的关闭事件
domClose(event) {
const isClickMenu = this.$refs.menu && this.$refs.menu.contains(event.target)
const isClickTrigger = this.$el.contains(event.target)
if (!isClickMenu && !isClickTrigger) this.show = false
},
autoSetClose() {
if (!this.autoClose) return
this.show = false
},
onMouseEnterTrigger(event) {
if (!this.triggerOnHover || this.disabled) return
// already showing
if (this.show) {
return clearTimeout(this.hideTimer)
}
// not showing
this.showTimer = setTimeout(() => {
this.show = true
}, 150)
},
onMouseLeaveTrigger(event) {
if (!this.triggerOnHover) return
this.setHideTimer()
clearTimeout(this.showTimer)
},
onMouseEnterDropdown() {
if (!this.triggerOnHover) return
clearTimeout(this.hideTimer)
},
onMouseLeaveDropdown() {
if (!this.triggerOnHover) return
this.setHideTimer()
},
setHideTimer() {
this.hideTimer = setTimeout(() => {
this.show = false
}, 200)
},
},
}
</script>
<style lang='sass' scoped>
.dropdown
display: inline-block
position: relative
.dropdown-link
display: inline-block
width: 100%
cursor: pointer
&.disabled
color: #CCC
cursor: not-allowed
.dropdown-content
max-width: 420px
position: absolute
background: #FFF
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.15)
border-radius: 2px
.userInfo
top: -7px !important
.space
top: -18px !important
</style>
<style lang="sass">
.dropdown-content
background-color: #FFF
&.list
.dropdown-menu
width: 100%
padding: 10px 0
ul
width: 100%
li, .drop-item
list-style: none
width: 100%
height: 32px
cursor: pointer
line-height: 32px
padding: 0 20px
text-align: left
color: #333
&:hover
background: #F5F7FA
color: #409EFF
&.disabled
color: #CCC
cursor: not-allowed
&:hover
// background: #F0F0F0
color: #CCC
// 对于表格内更多按钮的处理
table td
.dropdown-link.show
.more-button
color: #409EFF
</style>
<!--
当有网络请求在进行中时, 会禁用此按钮并显示 阻止文案
-->
<template lang="pug">
.btn(
:disabled="isDisable",
@click="trigger",
@mousedown="trigger",
@mouseup="trigger",
)
template(v-if="isDisable")
| {{ disabledLabel }}
slot(v-else)
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class ajaxButton extends Vue {
@Prop({ type: String, default: '处理中...' }) disabledLabel
get isDisable() {
return this.$store.getters.allRequestCount > 0
}
trigger(event) {
if (this.isDisable) return
this.$emit(event.type, event)
}
}
</script>
<template lang="pug">
table.date-table
thead
th(v-for="week in weeks") {{ week }}
tbody
tr(v-for="week in rows")
td(
v-for="day in week",
:class="{\
'pre-month': day.isPre,\
'available': day.isAvailable,\
'next-month': day.isNext || !day.isAvailable,\
'current': day.isAvailable && isCurrent(day.day),\
}",
@click="day.isAvailable && $emit('pick', day.day)",
)
div
span {{ day.day }}
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class DateTable extends Vue {
@Prop({ type: Date, required: true }) date
@Prop({ type: String, default: '' }) value
weeks: Array<string> = []
get year() { return this.date.getFullYear() }
get month() { return this.date.getMonth() }
get defaultValue() { return this.value ? new Date(this.value) : null }
// 计算当月的天数,以及空缺位置的上月和下月日期
get rows() {
// 获取当前日期
const today = new Date()
// 获取当前月份有多少天
const date = new Date(this.year, this.month + 1, 0)
const totalDay = date.getDate()
// 获取上个月有多少天
const preMonth = new Date(this.year, this.month, 0)
const preMonthDays = preMonth.getDate()
// 获取当前月份1号是星期几
const firstDay = new Date(this.year, this.month, 1)
const startDay = firstDay.getDay()
const prePlaceholder = startDay === 0 ? 6 : startDay
// 固定显示6行 42个
const nextPlaceholder = startDay === 0 ? 42 - 7 - totalDay : 42 - prePlaceholder - totalDay
const days: any[] = []
for (let i = 0; i < prePlaceholder; i++) {
days.unshift({ isPre: true, day: preMonthDays - i })
}
for (let i = 0; i < totalDay; i++) {
let isAvailable = false
if (this.year < today.getFullYear()) {
isAvailable = true
} else if (this.year === today.getFullYear()) {
if (this.month < today.getMonth()) {
isAvailable = true
} else if (this.month === today.getMonth()) {
if (i + 1 <= today.getDate()) isAvailable = true
}
}
days.push({ isAvailable: isAvailable, day: i + 1 })
}
for (let i = 0; i < nextPlaceholder; i++) {
days.push({ isNext: true, day: i + 1 })
}
return _.chunk(days, 7)
}
isCurrent(day) {
if (!this.defaultValue) return false
return this.defaultValue.getFullYear() === this.year &&
this.defaultValue.getMonth() === this.month &&
this.defaultValue.getDate() === day
}
@Watch('$route', { immediate: true })
auto() {
this.weeks = [vm.$t('public.seven'), vm.$t('public.one'), vm.$t('public.two'), vm.$t('public.three'), vm.$t('public.four'), vm.$t('public.five'), vm.$t('public.six')]
}
}
</script>
<style lang="sass" scoped>
.date-table
table-layout: fixed
width: 100%
color: #4C4C4C
th
text-align: center
vertical-align: middle
height: 32px
padding: 5px
border-bottom: 1px solid #E5E5E5
td
width: 32px
height: 32px
padding: 4px 0
&.pre-month, &.next-month
color: #CCC
&.available
cursor: pointer
&:hover
color: #409EFF
div
height: 30px
padding: 3px 0
box-sizing: border-box
span
text-align: center
margin: 0 auto
display: block
width: 24px
height: 24px
line-height: 24px
border-radius: 50%
&.current
span
color: #FFF
background-color: #409EFF
</style>
<template lang="pug">
table.month-table
tbody
tr(v-for="row in rows")
td(
style="width: 25%",
v-for="item in row",
@click="$emit('pick', item.index)",
:class='{ current: isCurrent(item.index) }',
)
span.cell {{ item.text }}
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class MonthTable extends Vue {
@Prop({ type: Date, required: true }) date
@Prop({ type: String, default: '' }) value
months: string[] = []
get defaultValue() { return this.value ? new Date(this.value) : null }
get rows() {
const months = this.months.map((item, index) => {
return {
text: item,
index: index,
}
})
return _.chunk(months, 4)
}
isCurrent(monthIndex) {
return this.defaultValue &&
this.defaultValue.getFullYear() === this.date.getFullYear() &&
monthIndex === this.date.getMonth()
}
@Watch('$route', { immediate: true, deep: true })
auto() {
this.months = [vm.$t('public.monthOne'), vm.$t('public.twoMonth'), vm.$t('public.monthThree'), vm.$t('public.fourMonth'), vm.$t('public.fiveMonth'), vm.$t('public.sixMonth'),
vm.$t('public.sevenMonth'), vm.$t('public.eightMonth'), vm.$t('public.nineMonth'), vm.$t('public.tenMonth'), vm.$t('public.elevenMonth'), vm.$t('public.twelveMonth')]
}
}
</script>
<style lang="sass" scoped>
.month-table
table-layout: fixed
width: 100%
color: #4C4C4C
td
padding: 20px 3px
cursor: pointer
&.current, &:hover
color: #409EFF
span
text-align: center
width: 48px
height: 32px
display: block
line-height: 32px
margin: 0 auto
</style>
<template lang="pug">
transition(name="zoom-in-top")
.picker-pancel
.picker-pancel-header.flex.justify(
:class="{ 'header-border': currentView !== 'date' }",
)
.picker-header-pre-btns
span.iconfont.icon-pre.pointer(
@click="preYear",
)
span.iconfont.icon-lastpicture.pointer(
v-show="currentView === 'date'",
@click="preMonth",
style="margin-left: 6px",
)
.picker-header-label
span.year(@click="currentView = 'year'") {{ yearLabel }}
span.month(
@click="currentView = 'month'",
v-show="currentView === 'date'",
) {{ month + 1 }}
.picker-header-next-btns
span.iconfont.icon-nextpicture.pointer(
v-show="currentView === 'date'",
@click="nextMonth",
style="margin-right: 6px",
)
span.iconfont.icon-next.pointer(
@click="nextYear",
)
.picker-pancel-content
date-table(
v-show="currentView === 'date'",
:date="date",
:value="value",
@pick="handleDatePick",
)
year-table(
v-show="currentView === 'year'",
:date="date",
:value="value",
@pick="handleYearPick",
)
month-table(
v-show="currentView === 'month'",
:date="date",
:value="value",
@pick="handleMonthPick",
)
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch, Model } from 'vue-property-decorator'
import dateTable from './_date-table.vue'
import yearTable from './_year-table.vue'
import monthTable from './_month-table.vue'
@Component({
components: {
dateTable,
yearTable,
monthTable,
},
})
export default class DatePanel extends Vue {
date: Date = new Date()
currentView: string = 'date'
@Prop({ type: String, default: '' }) value
@Watch('value', { immediate: true })
initDate(val) {
if (val) {
this.date = new Date(val)
}
}
get year() { return this.date.getFullYear() }
get month() { return this.date.getMonth() }
get yearLabel() {
if (this.currentView === 'year') {
const base = Math.floor(this.year / 10) * 10
return `${base} 年 - ${base + 10} 年`
}
return `${this.year} 年`
}
handleYearPick(year) {
this.setDate(year, this.month)
this.currentView = 'month'
}
handleMonthPick(month) {
this.setDate(this.year, month)
this.currentView = 'date'
}
handleDatePick(day) {
this.$emit('pick', `${this.year}-${this.month + 1}-${day}`)
}
preYear() {
if (this.currentView === 'year') {
this.setDate(this.year - 10, this.month)
} else {
this.setDate(this.year - 1, this.month)
}
}
nextYear() {
if (this.currentView === 'year') {
this.setDate(this.year + 10, this.month)
} else {
this.setDate(this.year + 1, this.month)
}
}
setDate(year, month) {
this.date = new Date(year, month, this.date.getDay(),
this.date.getMinutes(), this.date.getSeconds(), this.date.getMilliseconds())
}
preMonth() {
if (this.month === 0) {
this.setDate(this.year - 1, 12)
} else {
this.setDate(this.year, this.month - 1)
}
}
nextMonth() {
if (this.month === 11) {
this.setDate(this.year + 1, 0)
} else {
this.setDate(this.year, this.month + 1)
}
}
}
</script>
<style lang="sass" scoped>
.picker-pancel
z-index: 66
position: absolute
left: 0
transform: translateY(8px)
background: #FFFFFF
box-shadow: 0 0 8px 0 rgba(0,0,0,0.10)
border-radius: 2px
overflow: hidden
.picker-pancel-header
margin: 16px 25px 0
padding-bottom: 16px
font-size: 14px
&.header-border
border-bottom: 1px solid #E5E5E5
.picker-header-label
color: #262626
.year, .month
cursor: pointer
padding: 0 4px
&:hover
color: #409EFF
.iconfont
color: #B2B2B2
font-size: 12px
&:hover
color: #409EFF
.picker-pancel-content
width: 260px
position: relative
margin: 0 20px
font-size: 12px
.zoom-in-top-enter, .zoom-in-top-leave-to
max-height: 0
opacity: 0
.zoom-in-top-enter-to, .zoom-in-top-leave
max-height: 350px
opacity: 1
.zoom-in-top-enter-active, .zoom-in-top-leave-active
transition: all 0.3s
</style>
<template lang="pug">
table.year-table
tbody
tr(v-for="row in rows")
td(
style="width: 25%",
v-for="item in row",
:class="{ current: item === currentYear }",
@click="$emit('pick', item)",
)
span.cell {{ item }}
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
@Component
export default class YearTable extends Vue {
@Prop({ type: Date, required: true }) date
@Prop({ type: String, default: '' }) value
get year() { return this.date.getFullYear() }
get defaultValue() { return this.value ? new Date(this.value) : null }
get currentYear() { return this.defaultValue ? this.defaultValue.getFullYear() : null }
get rows() {
const base = Math.floor(this.year / 10) * 10
const years = Array.from({ length: 10 }).map((item, index) => base + index)
return _.chunk(years, 4)
}
}
</script>
<style lang="sass" scoped>
.year-table
table-layout: fixed
width: 100%
color: #4C4C4C
td
text-align: center
padding: 20px 3px
cursor: pointer
&.current, &:hover
color: #409EFF
span
text-align: center
width: 48px
height: 32px
display: block
line-height: 32px
margin: 0 auto
</style>
<!-- 日期组件
接收参数format 默认格式为 'YYYY-MM-DD'
-->
<template lang="pug">
.data-picker(:class="{ error: isError }")
.date-picker-slot(@click="show = true")
slot
.default-input
input.input(
type="text",
:value="value",
@focus="show = true",
:placeholder="placeholder",
)
span.iconfont.icon-date
panel(
v-show="show",
@pick="emit",
:value="value",
)
</template>
<script lang="ts">
import { Vue, Prop, Component, Watch, Model } from 'vue-property-decorator'
import panel from './_panel.vue'
@Component({
name: 'data-picker',
components: { panel },
})
export default class DataPicker extends Vue {
@Model('change', { type: String }) value
@Prop({ type: String, default: 'YYYY-MM-DD' }) format
@Prop({ type: String, default: '选择日期' }) placeholder
@Prop({ type: Boolean, default: false }) isError
show: boolean = false
emit(value) {
// 子组件传出的值为 YYYY-MM-DD格式
this.$emit('change', moment(value, 'YYYY-MM-DD').format(this.format))
this.show = false
}
mounted() {
document.addEventListener('click', this.close)
}
beforeDestroy() {
document.removeEventListener('click', this.close)
}
close(event) {
if (!this.$el.contains(event.target)) this.show = false
}
}
</script>
<style lang="sass" scoped>
.data-picker
display: inline-block
position: relative
.default-input
position: relative
.input
cursor: pointer
width: 100%
.icon-date
position: absolute
right: 16px
top: 50%
transform: translateY(-50%)
font-size: 14px
color: #CCC
pointer-events: none
&.error
.input
border-color: #FF4433
</style>
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!