initiate
3
.env.production
Normal file
@@ -0,0 +1,3 @@
|
||||
# 生产环境
|
||||
VITE_MODE = 'production'
|
||||
VITE_BASE_URL = '//ser.szzztec.com/stock'
|
||||
3
.env.test
Normal file
@@ -0,0 +1,3 @@
|
||||
# 测试环境
|
||||
VITE_MODE = 'test'
|
||||
VITE_BASE_URL = '//testser.szzztec.com/stock'
|
||||
18
.eslintrc.cjs
Normal file
@@ -0,0 +1,18 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off'
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
32
.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
.env.development
|
||||
auto-imports.d.ts
|
||||
components.d.ts
|
||||
package-lock.json
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
8
.prettierrc.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
45
README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# stock-h5
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run Unit Tests with [Vitest](https://vitest.dev/)
|
||||
|
||||
```sh
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
217
index.html
Normal file
@@ -0,0 +1,217 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover">
|
||||
<script src="//at.alicdn.com/t/c/font_4780705_wlsc4g1fnne.js"></script>
|
||||
<style>
|
||||
.sk-cube-grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 678px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube {
|
||||
width: 33%;
|
||||
height: 33%;
|
||||
float: left;
|
||||
-webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
|
||||
animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube1 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube2 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube3 {
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube4 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube5 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube6 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube7 {
|
||||
-webkit-animation-delay: 0;
|
||||
animation-delay: 0;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube8 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube9 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text1 {
|
||||
font-size: 18px;
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text2 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text3 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.sk-cube-grid {
|
||||
width: 750px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 {
|
||||
height: 180px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-cubeGridScaleDelay {
|
||||
|
||||
0%,
|
||||
70%,
|
||||
100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-cubeGridScaleDelay {
|
||||
|
||||
0%,
|
||||
70%,
|
||||
100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<title>数据管理系统</title>
|
||||
|
||||
<% for (const i of cdnCss) { %>
|
||||
<link href="<%= i %>" rel="stylesheet" />
|
||||
<% } %>
|
||||
<!-- 使用CDN的JS文件 -->
|
||||
<% for (const i of cdnJs) { %>
|
||||
<script src="<%= i %>" defer></script>
|
||||
<% } %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="grid" class="sk-cube-grid" style="height: calc(100vh);">
|
||||
<div>
|
||||
<div class="sk-cube-grid1">
|
||||
<div class="sk-cube sk-cube1">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="sk-cube sk-cube2">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="sk-cube sk-cube3">
|
||||
<img
|
||||
src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCABOAE8DASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD896KQHPPrz+NLX+f5/wBlBKnQ/X+gp9MTofr/AEFPreOy9F+QBRRRTAc38P8Auj+tNpzfw/7o/rTaxn8T+X5I438U/wDC/wAkRN0P++a/QH/gloM/ty/BQdPn+JX/AKp/4gGvz+bof98/yr7+/wCCXG7/AIbr+BmACNvxM46HP/Cn/iH/AJ/P2r6Xg+nz8Q4H/F87ezt/n1Xr2/mf6Wyt9H/ix9WqH4V6R8CRjPPIPQ7VzHx/cI/h9O+c9qm8v36+3/16rAkYZGCg84kby2PbkfxDp834DoKsh8Juk2qM4BBLA8Dvjr9B0we9eEoPZ6NaNNWd13VtD+nlVUkpQalBpOLvzXT6819biGM9iPx4/wAaZ0qbevHPUe/PWmsAwyP4ef8AP9KkuLk3d2s1dWt1t/wSOilwcZ7f5/z/AJFJQWSp0P1/oKfTE6H6/wBBT6DGfxP5fkhj9B9f6Gvvn/gltx+3R8EzjqfiUM4J/wCaQfEL0/CvgZ+g+v8AQ199f8Etf+T6Pgn9fiV/6qD4hV9LwV/yUGW/9f5/+kI/mT6XX/JhOLfTD/8Ap+mflp4N+JWh+L1WJJVg1VUAlsZ3EcTFVB81JZEjjm3ElAghhf5MlGBE0vogIOGUKjdGZjiP2VUl/c4GeCmOv5/m5DNJBJHJG0iyI25Gjk8uRWGCrIe5UjOPy61774I+Mt1aNBpviUHVIFAjivwqm4t8Af61JP3bFAMmX77Zx0UV/cHjT9E+vgXXzjgqMK+HftJVMJy8tWnGPvuMI9LNNpdb6bH+ev0Vv2kWW5nDL+EfFecKeLUYUY5tKUnTknyxg6kpO7bbTd9emp9UE8j5DtxwwBRSfUZ4I6DcnydhyDhcKRjAJ7YOfn6Hv1Hy9enrVLTdTsdWtEvdOukvI5lBDq5JRcAlWU8K4yCVT5ACD3NXdwGAWPGDuZdue2AO4/2u/I5r+DsfkuNy7E18JjsNUwlehUlSrQrX9pGcWk+a+nZ79j/YPIeI8n4qw2FzHhzNKGOwdajTrU/YNOk6c4xkuVptWto0+pKCCoPRgcEe/wDnjrRTo08xSUHOdxPtjGePp79Ce9JgnOB05+nWvMlD2bcObm5dObv5n07duW6abS07bb/rbrcSiggjr3GfwopBO6T+X42/pkjfcX8P5Gvvv/glr/yfR8E/r8Sv/VQfEKvgRvuL+H8jX31/wS3BP7c/wT4B+b4ldfb4P/EL057ivp+DdeIsut/z/l/6bR/MP0vP+TDcXdNMNr/3HpH86NGOQU+QngA8q2O317Z7cUUV/wBBdf2VWMsPUiqkJxaqRdHmsnbS/XS99t7dT/kDo1quGqwrUKtWOIhONSlOi3zUuW1l8t7bfr2fhjx94i8KTpJp1wTEcLcWVyc2dwgI3K0XG5yMBH/h4PbFfWngv4maH4vjjijk/s/UkjBlsLl41kLkDJt55v3cyEkhGHzfKU/hFfDIJGcd+DUtvPPazJPbTSW88Z3RyxP5bq4wflfnnjkZh7H7TFnn+b/FP6PfCvH+Fr1qOHpYDNoU5zw2IhD2PtaltFWkt220m99j+3vo5fTa8Q/BzMcLhcdmOMzDIKeIpKrhcW217DmScqd3okr6rU/SmOQICu1mUEHEIG9ScE+eR8rIODuHy8nqc0+Ry4ADZx8oW37dcebyPl5OffP1r5S8E/Ge4gMGneI2knjwI0vYDi5j2hQftR53IAMlt0mdxHmSY3n6a0rUrDVrZbqwuYLmFgGVrRuSCAT9o9xn5uOePcV/mL4j+DHFfAONlRzLLK0cK3JwxWFbnRqQVrTc1umt+2up/wBB3gN9LLw68bMuw88Fm2Gw2aQ5FUwmIrqFT2soRbhCLd1rtp5N9tBVKjBAGefl6YPp+INLUrK2AcAggFSpyAp4wPxHfPU+tRV+MOPI3C1uXS3a369/M/q2FZVYKUZKUZJSvzKWjs079V5+pI33F/D+Rr77/wCCW3/J9HwU+vxJ/wDVQfEKvgRvuL+H8jX33/wS1I/4bp+CY7k/Er8v+FP/ABC/+vX13AX/ACVOX+tT/wBIifzF9Lz/AJMLxf6Yf/09TP50KKKK/wCgiy7L7j/j+Ciiik3D+Zq2llFaenzRv8UUnqrL9Ou40qMqSTASfkmUYZmH3l3Yb5WGA/yTcc4iB3nq9A8YeIPDtxE9lcsYwyGPTGlLabNgnc2P3nOBlv8Aj556+TxjlgQoJCqxJ6Pkr9cc807G4iIAF5Ruw/MS47gDJzxjOK+fz7hzJOIsLVwedZbhswoVoOnF14KVSEZRStTb+Dvp8j6ngvxF4m8Ps7hmnDWZY/AYnDSp1+fD4l0qcpRkmozgneSdrNtd99L/AGj8P/ibpHiuNra/1GOw1qMnFhImIpvugNa8tlWbfGD5jkmM8r9xfWfnYEkf6sgkEYIyF6+vGD9CK/NhPNjuIjFIyXEcqpE0bCHEr4CuZdkrKF48vMUiRsC7wzgmOvoH4c/GDUbZdN0vxCk+p22oSRW+lanDsOqF5YYZY4tYgu5ZLS4tRHKhDJJLeRszxyXN20Yupv8AOvxx+ifDK/r3E3CVWjSwqqqu8vxFWKVKk7NqEpNXs02vif8AdTP9wvoiftFaPE+Jy3hHxFw2N/tKcaWBhjqFGpivrLSUY1JSpJuDtvBqMbL4mz6pQfu89t5GPfapz+v86+//APgl0cft1fAodifief8AzDnj/wDwFfAE7Z2llCPsUssZPlAFQw2BuR1zjoCcDgCvvj/glzkft1/Awj1+Jv8A6p34gf0NfxvwdhauA4yw+DqO8qNWvTlrf3opJ6ptPfR3en3H9/fSmzDCZp9HbiPH4ObqYfE0KNehNwnTcqc61PlbhUUZxfRqSVne2lmf/9k=" />
|
||||
</div>
|
||||
<div class="sk-cube sk-cube4">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube5">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube6">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube7">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube8">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube9">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
</div>
|
||||
<div class="text1">第一上海证券有限公司</div>
|
||||
<div class="text2">FIRST SHANGHAI SECURITIES LIMITED</div>
|
||||
<div class="text3">香港中環德輔道中 71 號永安集團大廈 19 樓D</div>
|
||||
<div class="text3">19/F, Wing On House, 71 Des Voeux Road Central, Hong Kong</div>
|
||||
<div class="text3">电话 Tel:(852)2532 1580 传真 Fax:(852)2537 6911</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
68
package.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "stock-h5",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --mode development",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"build:test": "vite build --mode test",
|
||||
"build:prod": "vite build --mode production",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "vitest",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build --force",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vant/auto-import-resolver": "^1.2.1",
|
||||
"@vant/touch-emulator": "^1.4.0",
|
||||
"axios": "^1.7.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.5.1",
|
||||
"markdown-it": "^14.1.0",
|
||||
"mitt": "^3.0.1",
|
||||
"moment": "^2.30.1",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"rollup-plugin-external-globals": "^0.10.0",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"sass": "^1.77.8",
|
||||
"sass-loader": "^16.0.0",
|
||||
"scss": "^0.2.4",
|
||||
"secure-ls": "^2.0.0",
|
||||
"unplugin-auto-import": "^0.17.6",
|
||||
"unplugin-vue-components": "^0.27.0",
|
||||
"vant": "^4.9.1",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-virtual-scroller": "^2.0.0-beta.8",
|
||||
"vue-wechat-title": "^2.0.7",
|
||||
"weixin-js-sdk": "^1.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.8.0",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
"@types/jsdom": "^21.1.6",
|
||||
"@types/node": "^20.12.5",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/test-utils": "^2.4.5",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"npm-run-all2": "^6.1.2",
|
||||
"prettier": "^3.2.5",
|
||||
"terser": "^5.31.1",
|
||||
"typescript": "~5.4.0",
|
||||
"vite": "^5.2.8",
|
||||
"vite-plugin-cdn-import": "^1.0.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vitest": "^1.4.0",
|
||||
"vue-tsc": "^2.0.11"
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
public/img/CLBG.jpeg
Normal file
|
After Width: | Height: | Size: 269 KiB |
BIN
public/img/GXBG.jpeg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
public/img/GXBG.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/img/SFBG.jpeg
Normal file
|
After Width: | Height: | Size: 466 KiB |
BIN
public/img/XLL.jpeg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/img/XLL.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/img/commentBg.jpg
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
public/img/completeLogo.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/img/headPic.jpg
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
public/img/home-banner.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/img/indicate.jpeg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/img/indicate.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
public/img/loginLogo.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/img/night.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/img/smallLogo.jpg
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
public/img/user-bg.jpeg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/img/user.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
276
src/App.vue
Normal file
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<van-overlay :show="show" z-index="999">
|
||||
<div class="sk-cube-grid">
|
||||
<div class="sk-cube-grid1">
|
||||
<div class="sk-cube sk-cube1">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="sk-cube sk-cube2">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="sk-cube sk-cube3">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="sk-cube sk-cube4">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube5">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube6">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube7">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube8">
|
||||
<img
|
||||
src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCABPAE8DASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8/k/1Uf0P86WmpkKqnGB065weef8AI/LFOr+B73s32X5H/ZJSt7Km1s4Rf3pMKKKKX8/+Bfmy5bP0f5Dk/wBSf+ud3/6LSvzMP3D9Xr9M0/1J/wCud3/6LSvzMP3D9Xr/AEX+gmk5Z/fX92vyif4Z/tcf4PBHrV/KYifcX/dX+Qp1NT7i/wC6v8hTq/0cSt7O3WMW/nuf4Ty2j6f5BRRRW2I0w7a3bvfztE1wvxw/6+fqfpajFgCeeFOcY+8M0+o4+AB/sp/6DUlf85M1apUS2U5JeSP+5yirUaSWyp00v/AUFFFFR/P/AIF+bLls/R/kOT/Un/rnd/8AotK/Mw/cP1ev0zT/AFJ/653f/otK/Mw/cP1ev9F/oJfFxB/17X5RP8M/2uP8Hgj1q/lMRPuL/ur/ACFOpqfcX/dX+Qp1f6OL/l3/AIYH+E8to+n+QUUUVtif92f9dImuF+OH/Xz9T9K4uR+C/oMVLUUXT8F/kalr/nKqpKrU85t/M/7nKP8ACpf9e4f+koKKKKz/AJ/8C/Nly2fo/wAhyf6k/wDXO7/9FpX5mH7h+r1+maf6k/8AXO7/APRaV+Zh+4fq9f6L/QS+LiD/AK9r8on+GH7XJv2XBOuzq2/8AqP8xE+4v+6v8hTqan3F/wB1f5CnV/o4v+Xf+GB/hS9Yw/wr8kFFFFbYn/dn/XSJthfjh/18/U/SuLp+C/yNS1FF0/Bf5Gpa/wCcqq71JvvJn/c3R/g0b7+yp3/8AQUUUVn/AD/4F+bNJbP0f5Dk5i9gtwreo3IvT8AMcdT6V+ZmD5QY9y//AOqv0zi/1Lf9tfx/dj/9X4V+ZuD5CnPGX4/z/n9a/wBFvoJfHmv9+M1P+8ko2v6H+F/7XP8AhcE+tX/03VGp9xf91f5CnU1fup/uL/6CM/rTq/0Zi37OD63XyScbL5H+FT+GH+FfkgooorqxP+7P+ukTbC/HD/r5+p+lqDCrnrtUNjoCBgj8DkH3FPoAG1T3JbOepO4/hRX/ADk1Facv7zur6b67br033P8AufikoxSVkopJbWSVkrBRRRUdJ/4F+bCWz9H+Q5P9UD6JcnHYlUUjP5kH2r8y8Yjxk8F/yr9NE/1J/wCud3/6LSvzMP3D9Xr/AEX+gn8Wev8AkheH91tRvb1P8L/2uf8AC4J9av8A6bqiJ9xf91f5CnU1PuL/ALq/yFOr/Rq2kV0tF/NpNv7z/Cp/DD/CvyQUUUV0L95ScH7z6J+qX4W/XudGFspptaXva3mtf+GP/9k=">
|
||||
</div>
|
||||
<div class="sk-cube sk-cube9">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-overlay>
|
||||
<div class="page-bg">
|
||||
<RouterView v-slot="{ Component }">
|
||||
<keep-alive>
|
||||
<component :is="Component" v-wechat-title="$route.meta.title" v-if="$route.meta.keepAlive" :key="$route.name" />
|
||||
</keep-alive>
|
||||
<component :is="Component" v-wechat-title="$route.meta.title" v-if="!$route.meta.keepAlive" :key="$route.name" />
|
||||
</RouterView>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import emitter from './utils/mitt'
|
||||
import wx from 'weixin-js-sdk'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
watch(() => route.fullPath, () => {
|
||||
const title = route.meta.title
|
||||
if (title === '点评详情' || title === '会议详情' || title === '会议' || title === '沟通详情' || title === '组合详情' ||
|
||||
title === '重点公司追踪详情') return
|
||||
console.log(1111111111);
|
||||
|
||||
setTitle({ title })
|
||||
})
|
||||
onMounted(async () => {
|
||||
})
|
||||
let show = ref(false)
|
||||
let loadingText = ref('加载中...')
|
||||
|
||||
const showLoading = (e: string) => {
|
||||
loadingText.value = e ? e : loadingText.value
|
||||
show.value = true
|
||||
}
|
||||
|
||||
const hiddenLoading = () => {
|
||||
show.value = false
|
||||
}
|
||||
|
||||
emitter.on('showLoading', showLoading)
|
||||
emitter.on('hiddenLoading', hiddenLoading)
|
||||
import reportInfoStore from '@/stores/reportInfo'
|
||||
const reportInfo = reportInfoStore()
|
||||
import fileInfoStore from '@/stores/fileInfo'
|
||||
const fileInfo = fileInfoStore()
|
||||
import stockInfoStore from '@/stores/stockInfo'
|
||||
const stockInfo = stockInfoStore()
|
||||
import speakInfoStore from '@/stores/speakInfo'
|
||||
const speakInfo = speakInfoStore()
|
||||
import listRefreshInfoStore from '@/stores/listRefreshInfo'
|
||||
const listRefreshInfo = listRefreshInfoStore()
|
||||
const setTitle = (e: any) => {
|
||||
wx.ready(() => {
|
||||
wx.updateAppMessageShareData({
|
||||
title: '第一上海证券',
|
||||
desc: e.title || '第一上海证券内部数据管理平台',
|
||||
link: 'http://h5.szzztec.com' + route.fullPath,
|
||||
imgUrl: 'http://h5.szzztec.com/img/smallLogo.jpg',
|
||||
success: function () {
|
||||
console.log('分享成功')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
emitter.on('setTitle', setTitle)
|
||||
onBeforeUnmount(() => {
|
||||
reportInfo.reportId = ''
|
||||
reportInfo.index = 0
|
||||
speakInfo.speakId = ''
|
||||
speakInfo.index = 0
|
||||
fileInfo.fileId = ''
|
||||
stockInfo.id = ''
|
||||
listRefreshInfo.addId = ''
|
||||
listRefreshInfo.deleteId = ''
|
||||
listRefreshInfo.refreshId = ''
|
||||
emitter.off('showLoading')
|
||||
emitter.off('hiddenLoading')
|
||||
emitter.off('setTitle')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.block {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.sk-cube-grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube {
|
||||
width: 33%;
|
||||
height: 33%;
|
||||
float: left;
|
||||
-webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
|
||||
animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube1 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube2 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube3 {
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube4 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube5 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube6 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube7 {
|
||||
-webkit-animation-delay: 0;
|
||||
animation-delay: 0;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube8 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube9 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 .sk-cube img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text1 {
|
||||
font-size: 18px;
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text2 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text3 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.sk-cube-grid {
|
||||
width: 750px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube-grid1 {
|
||||
height: 180px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.sk-cube-grid .text3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-cubeGridScaleDelay {
|
||||
|
||||
0%,
|
||||
70%,
|
||||
100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-cubeGridScaleDelay {
|
||||
|
||||
0%,
|
||||
70%,
|
||||
100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
542
src/assets/base.css
Normal file
@@ -0,0 +1,542 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
--van-gray-3: #dcdee0;
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
--safe-area-inset-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* vant */
|
||||
.van-popup--bottom {
|
||||
@media screen and (min-width: 992px) {
|
||||
left: calc(50vw - 496px) !important;
|
||||
right: calc(50vw - 496px) !important;
|
||||
width: 992px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.box-popup {
|
||||
min-height: 372px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.page-bg {
|
||||
.van-tag {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.tabbar-list {
|
||||
padding-bottom: 70px;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.page-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #ededed;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.page-content {
|
||||
background-color: #F2F3F5;
|
||||
max-width: 992px;
|
||||
margin: 0 auto;
|
||||
height: 100vh;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.page-div {
|
||||
background-color: #F2F3F5;
|
||||
max-width: 992px;
|
||||
margin: 0 auto;
|
||||
height: 100vh;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* .van-cell {
|
||||
margin: 0 16px 12px;
|
||||
width: calc(100% - 32px);
|
||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .1);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
width: 642px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
} */
|
||||
|
||||
.card {
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
/* width: calc(100% - 32px); */
|
||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .1);
|
||||
}
|
||||
.col-6 {
|
||||
width: 50%;
|
||||
}
|
||||
.col-12 {
|
||||
width: 100%;
|
||||
}
|
||||
.col-4 {
|
||||
width: 33.3%;
|
||||
}
|
||||
.van-tabbar {
|
||||
height: 60px !important;
|
||||
.van-icon {
|
||||
font-size: 26px !important;
|
||||
}
|
||||
.van-tabbar-item__text {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.van-cell {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.top-select-list {
|
||||
padding-top: 40px;
|
||||
}
|
||||
.text-ellipsis {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.top-select {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 40px;
|
||||
z-index: 4;
|
||||
background-color: #fff;
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
left: calc(50vw - 339px);
|
||||
right: calc(50vw - 339px);
|
||||
}
|
||||
|
||||
.van-grid-item__content {
|
||||
padding: 0;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
|
||||
.grid-item-text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
max-width: calc(33vw - 28px);
|
||||
font-size: 12px;
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.verticalSwiper {
|
||||
background-color: #fff;
|
||||
width: 100vh !important;
|
||||
height: 100vh;
|
||||
transform-origin: 50vw 50vw;
|
||||
transform: rotate(90deg) !important;
|
||||
overflow: hidden;
|
||||
.top-select {
|
||||
display: none;
|
||||
}
|
||||
.chart {
|
||||
width: 100vh;
|
||||
height: 100vw;
|
||||
}
|
||||
|
||||
.chartDiv {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.horizontalSwiper {
|
||||
background-color: #fff;
|
||||
.top-select {
|
||||
width: 100px;
|
||||
}
|
||||
.chart {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chartDiv {
|
||||
width: 100%;
|
||||
height: 40vh;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
position: relative;
|
||||
.icon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: #f1f1f1;
|
||||
padding: 10px;
|
||||
border-radius: 100%;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.icon-up {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.icon-down {
|
||||
bottom: 20px;
|
||||
}
|
||||
.loading {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
.custom-style {
|
||||
padding: 10px;
|
||||
|
||||
}
|
||||
ol {
|
||||
padding-left: 10px !important;
|
||||
li {
|
||||
list-style-type: decimal;
|
||||
list-style-position: inside;
|
||||
}
|
||||
}
|
||||
|
||||
/* iconfont */
|
||||
.iconfont {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* font-size */
|
||||
.content {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.font-12 {
|
||||
font-size: 12px;
|
||||
}
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* color */
|
||||
.primary-text-color {
|
||||
color: #1989fa !important;
|
||||
}
|
||||
|
||||
.disabled-primary-background-color {
|
||||
background-color: #90d7ec !important;
|
||||
}
|
||||
|
||||
.primary-background-color {
|
||||
background-color: #1989fa !important;
|
||||
}
|
||||
|
||||
.warning-text-color {
|
||||
color: #ff976a !important;
|
||||
}
|
||||
|
||||
.warning-background-color {
|
||||
background-color: #ff976a !important;
|
||||
}
|
||||
|
||||
.success-text-color {
|
||||
color: #07c160 !important;
|
||||
}
|
||||
|
||||
.success-background-color {
|
||||
background-color: #07c160 !important;
|
||||
}
|
||||
|
||||
.danger-text-color {
|
||||
color: #f56c6c !important;
|
||||
}
|
||||
|
||||
.danger-background-color {
|
||||
background-color: #f56c6c !important;
|
||||
}
|
||||
|
||||
.balck-text-color {
|
||||
color: #130c0e !important;
|
||||
}
|
||||
|
||||
.yellow-background-color {
|
||||
background-color: #e6a23c !important;
|
||||
}
|
||||
|
||||
.pink-background-color {
|
||||
background-color: #f173ac !important;
|
||||
}
|
||||
|
||||
.purple-background-color {
|
||||
background-color: #8552a1 !important;
|
||||
}
|
||||
|
||||
.other-background-color {
|
||||
background-color: #8390e5 !important;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #1989fa;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.activeColor {
|
||||
color: #1989fa;
|
||||
}
|
||||
|
||||
.white-text-color {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.disbaled-text-color {
|
||||
color: #c8c9cc;
|
||||
}
|
||||
|
||||
.disbaled-background-color {
|
||||
background-color: #c8c9cc;
|
||||
}
|
||||
|
||||
/* flex */
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.justify-content-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-content-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-content-end {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.align-items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.align-items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.align-items-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/* padding */
|
||||
|
||||
.p-10 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.p-8 {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.ps-16 {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.pe-16 {
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.pt-5 {
|
||||
padding-top: 5;
|
||||
}
|
||||
|
||||
.pb-5 {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
/* margin */
|
||||
.m-5 {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.m-16 {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.ms-10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mx-10 {
|
||||
margin: 0 10px;
|
||||
}
|
||||
/* border */
|
||||
.border-1 {
|
||||
border: 1px solid #dcdee0;
|
||||
}
|
||||
|
||||
.border-0 {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.border-bottom-1 {
|
||||
border-bottom: 1px solid #dcdee0;
|
||||
}
|
||||
|
||||
.border-right-1 {
|
||||
border-right: 1px solid #dcdee0;
|
||||
}
|
||||
|
||||
.border-radius-4 {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.border-radius-8 {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 省略号 */
|
||||
.ellipsis-one {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.addDiv {
|
||||
position: fixed;
|
||||
bottom: calc(env(safe-area-inset-bottom) + 70px);
|
||||
right: 20px;
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
border-radius: 100%;
|
||||
color: #fff;
|
||||
z-index: 2;
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
right: calc(50vw - 319px);
|
||||
}
|
||||
|
||||
.text-center {
|
||||
height: 30px;
|
||||
width: 60px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.addDiv1 {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.addDiv2 {
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
}
|
||||
539
src/assets/iconfont/demo.css
Normal file
@@ -0,0 +1,539 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
487
src/assets/iconfont/demo_index.html
Normal file
@@ -0,0 +1,487 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>iconfont Demo</title>
|
||||
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
|
||||
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<link rel="stylesheet" href="iconfont.css">
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
<style>
|
||||
.main .logo {
|
||||
margin-top: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main .logo .sub-title {
|
||||
margin-left: 0.5em;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: linear-gradient(-45deg, #3967FF, #B500FE);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
|
||||
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
|
||||
|
||||
</a></h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4540741" target="_blank" class="nav-more">查看项目</a>
|
||||
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">链接</div>
|
||||
<div class="code-name">&#xe600;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">良品铺子</div>
|
||||
<div class="code-name">&#xe61f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">向下双箭头</div>
|
||||
<div class="code-name">&#xe62d;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">月历</div>
|
||||
<div class="code-name">&#xe6c4;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">KHCFDC_音频文件</div>
|
||||
<div class="code-name">&#xe6ed;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">excel</div>
|
||||
<div class="code-name">&#xe8a6;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">image</div>
|
||||
<div class="code-name">&#xe8aa;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">pdf</div>
|
||||
<div class="code-name">&#xe8af;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">ppt</div>
|
||||
<div class="code-name">&#xe8b0;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">txt</div>
|
||||
<div class="code-name">&#xe8b4;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">unknown</div>
|
||||
<div class="code-name">&#xe8b5;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">word</div>
|
||||
<div class="code-name">&#xe8b6;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">默认文件夹</div>
|
||||
<div class="code-name">&#xe6ea;</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1755072825017') format('woff2'),
|
||||
url('iconfont.woff?t=1755072825017') format('woff'),
|
||||
url('iconfont.ttf?t=1755072825017') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="iconfont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-lianjie"></span>
|
||||
<div class="name">
|
||||
链接
|
||||
</div>
|
||||
<div class="code-name">.icon-lianjie
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-liangpinpuzi"></span>
|
||||
<div class="name">
|
||||
良品铺子
|
||||
</div>
|
||||
<div class="code-name">.icon-liangpinpuzi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xiangxiashuangjiantou"></span>
|
||||
<div class="name">
|
||||
向下双箭头
|
||||
</div>
|
||||
<div class="code-name">.icon-xiangxiashuangjiantou
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-yueli"></span>
|
||||
<div class="name">
|
||||
月历
|
||||
</div>
|
||||
<div class="code-name">.icon-yueli
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-yinpinwenjian"></span>
|
||||
<div class="name">
|
||||
KHCFDC_音频文件
|
||||
</div>
|
||||
<div class="code-name">.icon-yinpinwenjian
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-excel"></span>
|
||||
<div class="name">
|
||||
excel
|
||||
</div>
|
||||
<div class="code-name">.icon-excel
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-image"></span>
|
||||
<div class="name">
|
||||
image
|
||||
</div>
|
||||
<div class="code-name">.icon-image
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-pdf"></span>
|
||||
<div class="name">
|
||||
pdf
|
||||
</div>
|
||||
<div class="code-name">.icon-pdf
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-ppt"></span>
|
||||
<div class="name">
|
||||
ppt
|
||||
</div>
|
||||
<div class="code-name">.icon-ppt
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-txt"></span>
|
||||
<div class="name">
|
||||
txt
|
||||
</div>
|
||||
<div class="code-name">.icon-txt
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-unknown"></span>
|
||||
<div class="name">
|
||||
unknown
|
||||
</div>
|
||||
<div class="code-name">.icon-unknown
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-word"></span>
|
||||
<div class="name">
|
||||
word
|
||||
</div>
|
||||
<div class="code-name">.icon-word
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-morenwenjianjia"></span>
|
||||
<div class="name">
|
||||
默认文件夹
|
||||
</div>
|
||||
<div class="code-name">.icon-morenwenjianjia
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"
|
||||
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-lianjie"></use>
|
||||
</svg>
|
||||
<div class="name">链接</div>
|
||||
<div class="code-name">#icon-lianjie</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-liangpinpuzi"></use>
|
||||
</svg>
|
||||
<div class="name">良品铺子</div>
|
||||
<div class="code-name">#icon-liangpinpuzi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiangxiashuangjiantou"></use>
|
||||
</svg>
|
||||
<div class="name">向下双箭头</div>
|
||||
<div class="code-name">#icon-xiangxiashuangjiantou</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yueli"></use>
|
||||
</svg>
|
||||
<div class="name">月历</div>
|
||||
<div class="code-name">#icon-yueli</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yinpinwenjian"></use>
|
||||
</svg>
|
||||
<div class="name">KHCFDC_音频文件</div>
|
||||
<div class="code-name">#icon-yinpinwenjian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-excel"></use>
|
||||
</svg>
|
||||
<div class="name">excel</div>
|
||||
<div class="code-name">#icon-excel</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-image"></use>
|
||||
</svg>
|
||||
<div class="name">image</div>
|
||||
<div class="code-name">#icon-image</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-pdf"></use>
|
||||
</svg>
|
||||
<div class="name">pdf</div>
|
||||
<div class="code-name">#icon-pdf</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-ppt"></use>
|
||||
</svg>
|
||||
<div class="name">ppt</div>
|
||||
<div class="code-name">#icon-ppt</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-txt"></use>
|
||||
</svg>
|
||||
<div class="name">txt</div>
|
||||
<div class="code-name">#icon-txt</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-unknown"></use>
|
||||
</svg>
|
||||
<div class="name">unknown</div>
|
||||
<div class="code-name">#icon-unknown</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-word"></use>
|
||||
</svg>
|
||||
<div class="name">word</div>
|
||||
<div class="code-name">#icon-word</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-morenwenjianjia"></use>
|
||||
</svg>
|
||||
<div class="name">默认文件夹</div>
|
||||
<div class="code-name">#icon-morenwenjianjia</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.tab-container .content:first').show()
|
||||
|
||||
$('#tabs li').click(function (e) {
|
||||
var tabContent = $('.tab-container .content')
|
||||
var index = $(this).index()
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
return
|
||||
} else {
|
||||
$('#tabs li').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
|
||||
tabContent.hide().eq(index).fadeIn()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
67
src/assets/iconfont/iconfont.css
Normal file
@@ -0,0 +1,67 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4540741 */
|
||||
src: url('iconfont.woff2?t=1755072825017') format('woff2'),
|
||||
url('iconfont.woff?t=1755072825017') format('woff'),
|
||||
url('iconfont.ttf?t=1755072825017') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-lianjie:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-liangpinpuzi:before {
|
||||
content: "\e61f";
|
||||
}
|
||||
|
||||
.icon-xiangxiashuangjiantou:before {
|
||||
content: "\e62d";
|
||||
}
|
||||
|
||||
.icon-yueli:before {
|
||||
content: "\e6c4";
|
||||
}
|
||||
|
||||
.icon-yinpinwenjian:before {
|
||||
content: "\e6ed";
|
||||
}
|
||||
|
||||
.icon-excel:before {
|
||||
content: "\e8a6";
|
||||
}
|
||||
|
||||
.icon-image:before {
|
||||
content: "\e8aa";
|
||||
}
|
||||
|
||||
.icon-pdf:before {
|
||||
content: "\e8af";
|
||||
}
|
||||
|
||||
.icon-ppt:before {
|
||||
content: "\e8b0";
|
||||
}
|
||||
|
||||
.icon-txt:before {
|
||||
content: "\e8b4";
|
||||
}
|
||||
|
||||
.icon-unknown:before {
|
||||
content: "\e8b5";
|
||||
}
|
||||
|
||||
.icon-word:before {
|
||||
content: "\e8b6";
|
||||
}
|
||||
|
||||
.icon-morenwenjianjia:before {
|
||||
content: "\e6ea";
|
||||
}
|
||||
|
||||
1
src/assets/iconfont/iconfont.js
Normal file
100
src/assets/iconfont/iconfont.json
Normal file
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"id": "4540741",
|
||||
"name": "template-h5-stock",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "77403",
|
||||
"name": "链接",
|
||||
"font_class": "lianjie",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "10033597",
|
||||
"name": "良品铺子",
|
||||
"font_class": "liangpinpuzi",
|
||||
"unicode": "e61f",
|
||||
"unicode_decimal": 58911
|
||||
},
|
||||
{
|
||||
"icon_id": "14209038",
|
||||
"name": "向下双箭头",
|
||||
"font_class": "xiangxiashuangjiantou",
|
||||
"unicode": "e62d",
|
||||
"unicode_decimal": 58925
|
||||
},
|
||||
{
|
||||
"icon_id": "18977410",
|
||||
"name": "月历",
|
||||
"font_class": "yueli",
|
||||
"unicode": "e6c4",
|
||||
"unicode_decimal": 59076
|
||||
},
|
||||
{
|
||||
"icon_id": "9881679",
|
||||
"name": "KHCFDC_音频文件",
|
||||
"font_class": "yinpinwenjian",
|
||||
"unicode": "e6ed",
|
||||
"unicode_decimal": 59117
|
||||
},
|
||||
{
|
||||
"icon_id": "1344630",
|
||||
"name": "excel",
|
||||
"font_class": "excel",
|
||||
"unicode": "e8a6",
|
||||
"unicode_decimal": 59558
|
||||
},
|
||||
{
|
||||
"icon_id": "1344639",
|
||||
"name": "image",
|
||||
"font_class": "image",
|
||||
"unicode": "e8aa",
|
||||
"unicode_decimal": 59562
|
||||
},
|
||||
{
|
||||
"icon_id": "1344646",
|
||||
"name": "pdf",
|
||||
"font_class": "pdf",
|
||||
"unicode": "e8af",
|
||||
"unicode_decimal": 59567
|
||||
},
|
||||
{
|
||||
"icon_id": "1344647",
|
||||
"name": "ppt",
|
||||
"font_class": "ppt",
|
||||
"unicode": "e8b0",
|
||||
"unicode_decimal": 59568
|
||||
},
|
||||
{
|
||||
"icon_id": "1344654",
|
||||
"name": "txt",
|
||||
"font_class": "txt",
|
||||
"unicode": "e8b4",
|
||||
"unicode_decimal": 59572
|
||||
},
|
||||
{
|
||||
"icon_id": "1344655",
|
||||
"name": "unknown",
|
||||
"font_class": "unknown",
|
||||
"unicode": "e8b5",
|
||||
"unicode_decimal": 59573
|
||||
},
|
||||
{
|
||||
"icon_id": "1344657",
|
||||
"name": "word",
|
||||
"font_class": "word",
|
||||
"unicode": "e8b6",
|
||||
"unicode_decimal": 59574
|
||||
},
|
||||
{
|
||||
"icon_id": "22014791",
|
||||
"name": "默认文件夹",
|
||||
"font_class": "morenwenjianjia",
|
||||
"unicode": "e6ea",
|
||||
"unicode_decimal": 59114
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
src/assets/iconfont/iconfont.ttf
Normal file
BIN
src/assets/iconfont/iconfont.woff
Normal file
BIN
src/assets/iconfont/iconfont.woff2
Normal file
502
src/assets/index.scss
Normal file
@@ -0,0 +1,502 @@
|
||||
.page-div {
|
||||
.back-icon {
|
||||
position: fixed;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
z-index: 200;
|
||||
@media screen and (min-width: 992px) {
|
||||
left: calc(50vw - 491px)
|
||||
}
|
||||
&.tapped {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
i {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 24px 16px 16px;
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, #165DFF 0%, #0FC6C2 100%);
|
||||
color: white;
|
||||
border-radius: 0 0 24px 24px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 16px;
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 24px 16px 16px;
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background-color: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
margin: 0 16px 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
|
||||
.card-title {
|
||||
font-size: clamp(14px, 3.5vw, 16px);
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 20px;
|
||||
margin: 0 16px 16px;
|
||||
|
||||
.card-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 6px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.loading {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.chartDiv {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.tab-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.tab-button {
|
||||
flex: 1;
|
||||
font-size: clamp(12px, 3vw, 14px);
|
||||
padding: 12px 0;
|
||||
transition: all 0.2s ease;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
color: #165DFF;
|
||||
font-weight: 500;
|
||||
border-bottom: 2px solid #165DFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
|
||||
.van-grid-item__content {
|
||||
border-radius: 16px;
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
|
||||
&.tapped {
|
||||
transform: scale(0.9);
|
||||
opacity: 0.7;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
background-color: rgba(240, 240, 240, 0.5);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
border-left: 4px solid;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.card-code {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-note {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 18px;
|
||||
|
||||
.card-code {
|
||||
font-size: 18px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 15px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.card-note {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.card-note {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-morenwenjianjia,
|
||||
.icon-folder6wenjianjia {
|
||||
color: #72cffb;
|
||||
}
|
||||
.icon-ppt {
|
||||
color: #e34221;
|
||||
}
|
||||
.icon-pdf {
|
||||
color: #8c181a;
|
||||
}
|
||||
.icon-excel {
|
||||
color: #45b058;
|
||||
}
|
||||
.icon-txt {
|
||||
color: #f9ca06;
|
||||
}
|
||||
.icon-word {
|
||||
color: #14a9da;
|
||||
}
|
||||
.icon-unknown {
|
||||
color: #7d99af;
|
||||
}
|
||||
.icon-image {
|
||||
color: #49c9a7;
|
||||
}
|
||||
|
||||
.red-color {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.blue-color {
|
||||
color: blue !important;
|
||||
}
|
||||
|
||||
.green-color {
|
||||
color: green !important;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.financial-data {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px 8px;
|
||||
}
|
||||
|
||||
.data-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
background-color: #f9fafb;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.financial-data {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
.data-item {
|
||||
padding: 12px;
|
||||
}
|
||||
.data-label {
|
||||
font-size: 13px;
|
||||
}
|
||||
.data-value {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tableDiv {
|
||||
overflow: auto;
|
||||
table {
|
||||
border: 1px solid #808080;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
thead {
|
||||
text-align: center;
|
||||
|
||||
tr {
|
||||
th {
|
||||
border: 1px solid #757575;
|
||||
font-weight: 400;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
position: sticky;
|
||||
top: -1px;
|
||||
background-color: #fff;
|
||||
z-index: 2;
|
||||
width: 100px;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
position: sticky;
|
||||
left: -1px;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
text-align: center;
|
||||
|
||||
tr {
|
||||
td {
|
||||
border: 1px solid #757575;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
z-index: 2;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
&.td--column--1 {
|
||||
position: sticky;
|
||||
left: -1px;
|
||||
background-color: #fff;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
text-align: center;
|
||||
|
||||
tr {
|
||||
td {
|
||||
border: 1px solid #757575;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
z-index: 2;
|
||||
|
||||
&.td--column--1 {
|
||||
position: sticky;
|
||||
left: -1px;
|
||||
background-color: #fff;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.d-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.align-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.align-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.text-start {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-end {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.f-b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.big-title {
|
||||
font-size: 20px;
|
||||
@media screen and (min-width: 768px) {
|
||||
font-size: 24px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
.normal-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.p-16 {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.pt-16 {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.pb-16 {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mb-16 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mb-8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
1
src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
39
src/components/back-button.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="back-icon" @touchstart="handleTouchStart" @touchend="handleTouchEnd"
|
||||
@touchcancel="handleTouchCancel" :class="{ 'tapped': isTouched }">
|
||||
<van-icon name="arrow-left" class="icon" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
const isTouched = ref(false)
|
||||
|
||||
const handleTouchStart = () => {
|
||||
isTouched.value = true
|
||||
}
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
if (isTouched.value) {
|
||||
setTimeout(() => {
|
||||
if (window.history.length <= 1) {
|
||||
router.push({ name: 'home' })
|
||||
} else {
|
||||
router.back()
|
||||
}
|
||||
isTouched.value = false
|
||||
}, 150)
|
||||
}
|
||||
}
|
||||
|
||||
const handleTouchCancel = () => {
|
||||
isTouched.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
</style>
|
||||
58
src/components/communicate-item.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div v-if="list.length > 0">
|
||||
<van-cell v-for="item in list" :key="item.linkId"
|
||||
@click="$router.push({ name: 'communicate-detail', query: { linkId: item.linkId } })">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="name1 text-start">
|
||||
<van-tag :class="item.tagColor" size="medium">{{ item.linkTypeName }}</van-tag>
|
||||
</div>
|
||||
<div class="name1 stockNames balck-text-color">{{ item.stockNames ? item.stockNames : '' }}</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between my-2">
|
||||
<div class="van-ellipsis name2 flex-1 text-start balck-text-color">{{ item.cusName }}</div>
|
||||
<div class="van-ellipsis name2 flex-1 text-end balck-text-color">{{ item.cusUserName }}</div>
|
||||
</div>
|
||||
<div class="van-multi-ellipsis--l2 note my-2 text-start">{{ item.linkNote }}</div>
|
||||
</van-cell>
|
||||
</div>
|
||||
<van-empty v-else description="暂无沟通" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
list: {
|
||||
type: Array<any>,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.van-cell {
|
||||
.name1 {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
}
|
||||
.stockNames {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.note {
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
.empty {
|
||||
color: #808080;
|
||||
font-size: 14px;
|
||||
}
|
||||
.my-2 {
|
||||
margin: 2px 0;
|
||||
}
|
||||
:deep(.van-empty__image) {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
</style>
|
||||
140
src/components/file-list.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div>
|
||||
<van-grid v-if="dataList.length > 0">
|
||||
<van-grid-item v-for="item in dataList" :key="item.fileId" @click="toShowFile(item)" :id="item.fileId">
|
||||
<i class="iconfont" :class="`icon-${item.type}`" />
|
||||
<div class="grid-item-text mt-10">{{ item.name }}</div>
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
<van-empty v-else image-size="80" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import fileInfoStore from '@/stores/fileInfo'
|
||||
const fileInfo = fileInfoStore()
|
||||
import { workFileListByMenuId } from '@/utils/api'
|
||||
let dataList = ref<{ fileId: '', type: '', name: '' }[]>([])
|
||||
const init = async (menuId: any) => {
|
||||
const { data } = await workFileListByMenuId({ menuId: menuId })
|
||||
data.menuList.map((ele: any) => {
|
||||
ele.type = 'morenwenjianjia'
|
||||
ele.name = ele.menuName
|
||||
})
|
||||
const reg1 = /(\.xls|\.xlsx)$/
|
||||
const reg2 = /(\.txt)$/
|
||||
const reg3 = /(\.pdf)$/
|
||||
const reg4 = /(\.doc|\.docx)$/
|
||||
const reg5 = /(\.ppt|\.pptx)$/
|
||||
const reg6 = /(\.jpg|\.png)$/
|
||||
const reg7 = /(\.mp3|\.m4a)$/
|
||||
data.fileList.map((ele: any) => {
|
||||
ele.type = 'unknown'
|
||||
ele.name = ele.fileName
|
||||
if (reg1.test(ele.fileName)) {
|
||||
ele.type = 'excel'
|
||||
ele.name = ele.fileName.replace(reg1, '')
|
||||
}
|
||||
if (reg2.test(ele.fileName)) {
|
||||
ele.type = 'txt'
|
||||
ele.name = ele.fileName.replace(reg2, '')
|
||||
}
|
||||
if (reg3.test(ele.fileName)) {
|
||||
ele.type = 'pdf'
|
||||
ele.name = ele.fileName.replace(reg3, '')
|
||||
}
|
||||
if (reg4.test(ele.fileName)) {
|
||||
ele.type = 'word'
|
||||
ele.name = ele.fileName.replace(reg4, '')
|
||||
}
|
||||
if (reg5.test(ele.fileName)) {
|
||||
ele.type = 'ppt'
|
||||
ele.name = ele.fileName.replace(reg5, '')
|
||||
}
|
||||
if (reg6.test(ele.fileName)) {
|
||||
ele.type = 'image'
|
||||
ele.name = ele.fileName.replace(reg6, '')
|
||||
}
|
||||
if (reg7.test(ele.fileName)) {
|
||||
ele.type = 'yinpinwenjian'
|
||||
ele.name = ele.fileName.replace(reg7, '')
|
||||
}
|
||||
})
|
||||
dataList.value = [...data.menuList, ...data.fileList]
|
||||
}
|
||||
|
||||
import { showFile } from '@/mixins/show-file'
|
||||
const { loadingFile } = showFile()
|
||||
const toShowFile = (item: any) => {
|
||||
if (item.type === 'morenwenjianjia') {
|
||||
showToast('暂时不能打开文件夹')
|
||||
} else if (item.type === 'unknown') {
|
||||
showToast('未知文件无法打开')
|
||||
} else {
|
||||
fileInfo.fileId = item.fileId
|
||||
loadingFile(`${import.meta.env.VITE_BASE_URL}/api/file/${item.fileId}`)
|
||||
}
|
||||
}
|
||||
defineExpose({ init })
|
||||
</script>
|
||||
<style lang='scss' scoped>
|
||||
:deep(.van-grid-item__content) {
|
||||
padding: 8px 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.grid-item-text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
max-width: calc(25vw - 10px);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
max-width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size: 30px !important;
|
||||
}
|
||||
|
||||
.icon-morenwenjianjia,
|
||||
.icon-folder6wenjianjia {
|
||||
color: #72cffb;
|
||||
}
|
||||
|
||||
.icon-ppt {
|
||||
color: #e34221;
|
||||
}
|
||||
|
||||
.icon-pdf {
|
||||
color: #8c181a;
|
||||
}
|
||||
|
||||
.icon-excel {
|
||||
color: #45b058;
|
||||
}
|
||||
|
||||
.icon-txt {
|
||||
color: #f9ca06;
|
||||
}
|
||||
|
||||
.icon-word {
|
||||
color: #14a9da;
|
||||
}
|
||||
|
||||
.icon-image {
|
||||
color: #49c9a7;
|
||||
}
|
||||
|
||||
.icon-unknown {
|
||||
color: #7d99af;
|
||||
}
|
||||
|
||||
.van-empty {
|
||||
padding: 10px 0
|
||||
}
|
||||
</style>
|
||||
21
src/components/tabbar.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<van-tabbar route>
|
||||
<van-tabbar-item replace to="/" icon="home-o">首页</van-tabbar-item>
|
||||
<van-tabbar-item replace to="/target-table" icon="guide-o">指标</van-tabbar-item>
|
||||
<van-tabbar-item replace to="/user" icon="friends-o">我的</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.van-tabbar {
|
||||
height: 60px;
|
||||
max-width: 992px;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
@media screen and (min-width: 992px) {
|
||||
left: calc(50vw - 496px) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
44
src/components/top-date1.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="grid-item-text" @click="showDate">{{ name }}</div>
|
||||
<van-icon name="close" v-if="name !== initialName" @click="clearName" />
|
||||
<van-icon name="arrow-down" v-else @click="showDate" />
|
||||
</div>
|
||||
<van-calendar v-model:show="showCalendar" @select="onDateConfirm" :show-confirm="false" switch-mode="year-month"
|
||||
:default-date="defaultDate" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import moment from 'moment'
|
||||
let showCalendar = ref(false)
|
||||
let name: any = ref('')
|
||||
let defaultDate: any = ref()
|
||||
let prop = defineProps({
|
||||
initialName: String,
|
||||
selectDate: String
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
name.value = prop.selectDate
|
||||
})
|
||||
})
|
||||
|
||||
const showDate = () => {
|
||||
defaultDate.value = name.value === prop.initialName ? null : new Date(name.value)
|
||||
showCalendar.value = true
|
||||
}
|
||||
const clearName = () => {
|
||||
name.value = prop.initialName
|
||||
emit('refresh', name.value)
|
||||
}
|
||||
const onDateConfirm = (e: any) => {
|
||||
name.value = moment(e).format('YYYY-MM-DD')
|
||||
showCalendar.value = false
|
||||
emit('refresh', name.value)
|
||||
}
|
||||
const emit = defineEmits(['refresh'])
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
118
src/components/top-select1.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="grid-item-text" @click="showPicker">{{ name }}</div>
|
||||
<van-icon name="close" v-if="name !== initialName && type !== 'echart_date' && type !== 'stock'"
|
||||
@click="clearName" />
|
||||
<van-icon name="arrow-down" v-else @click="showPicker" />
|
||||
</div>
|
||||
<van-popup v-model:show="showPickerFlag" position="bottom">
|
||||
<van-picker show-toolbar :title="initialName" :columns="list" @confirm="onConfirm"
|
||||
@cancel="showPickerFlag = false" v-model="value" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
let showPickerFlag = ref(false)
|
||||
let name: any = ref('')
|
||||
let value = ref<string[]>([])
|
||||
let popupValue: any = ref('')
|
||||
let list: any = ref([])
|
||||
let prop = defineProps({
|
||||
type: String,
|
||||
initialName: String,
|
||||
selectName: String,
|
||||
selectValue: String
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
name.value = prop.selectName
|
||||
popupValue.value = prop.selectValue
|
||||
setList()
|
||||
})
|
||||
})
|
||||
|
||||
import { sysDicListByType, sysUserGetSaleAndManagerList, workStaffLookup } from '@/utils/api'
|
||||
const setList = async () => {
|
||||
if (prop.type === 'sale') {
|
||||
const { data } = await sysUserGetSaleAndManagerList()
|
||||
list.value = data.map((ele: any) => {
|
||||
ele.text = ele.userName
|
||||
ele.value = ele.userId
|
||||
return ele
|
||||
})
|
||||
} else if (prop.type === 'echart_date') {
|
||||
list.value = [{
|
||||
text: '最近10年',
|
||||
value: '1'
|
||||
}, {
|
||||
text: '最近1年',
|
||||
value: '2'
|
||||
}, {
|
||||
text: '最近半年',
|
||||
value: '3'
|
||||
}, {
|
||||
text: '最近3个月',
|
||||
value: '4'
|
||||
}, {
|
||||
text: '最近1个月',
|
||||
value: '5'
|
||||
}, {
|
||||
text: '最近1周',
|
||||
value: '6'
|
||||
}]
|
||||
} else if (prop.type === 'link_type') {
|
||||
list.value = [{
|
||||
text: '上市公司按需会议',
|
||||
value: '31'
|
||||
}, {
|
||||
text: '机构路演',
|
||||
value: '32'
|
||||
}, {
|
||||
text: '机构沟通',
|
||||
value: '34'
|
||||
}]
|
||||
} else if (prop.type === 'create_user') {
|
||||
const { data } = await workStaffLookup()
|
||||
list.value = data.map((ele: any) => {
|
||||
ele.text = ele.staffName
|
||||
ele.value = ele.staffId
|
||||
return ele
|
||||
})
|
||||
} else {
|
||||
const { data } = await sysDicListByType({ dicType: prop.type })
|
||||
list.value = data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
if (prop.type === 'meeting_type') {
|
||||
list.value.push({
|
||||
text: '全选',
|
||||
value: 'all'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const showPicker = () => {
|
||||
value.value = [`${popupValue.value}`]
|
||||
showPickerFlag.value = true
|
||||
}
|
||||
const clearName = () => {
|
||||
name.value = prop.initialName
|
||||
popupValue.value = ''
|
||||
emit('refresh', { value: popupValue.value, name: name.value })
|
||||
}
|
||||
const onConfirm = (e: any) => {
|
||||
const { text, value, cid } = e.selectedOptions[0]
|
||||
name.value = text
|
||||
popupValue.value = value
|
||||
showPickerFlag.value = false
|
||||
emit('refresh', { value: popupValue.value, name: text, cid })
|
||||
}
|
||||
const emit = defineEmits(['refresh'])
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
90
src/components/top-select2.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="grid-item-text" @click="showPicker">{{ name }}</div>
|
||||
<van-icon name="close" v-if="name !== initialName" @click="clearName" />
|
||||
<van-icon name="arrow-down" v-else @click="showPicker" />
|
||||
</div>
|
||||
<van-popup v-model:show="showPickerFlag" position="bottom">
|
||||
<van-field v-model="searchValue" :placeholder="'请输入' + initialName" @input="onSearchInput" />
|
||||
<van-picker show-toolbar :title="initialName + '列表'" :columns="list" @confirm="onConfirm"
|
||||
@cancel="showPickerFlag = false" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
let showPickerFlag = ref(false)
|
||||
let name: any = ref('')
|
||||
let popupValue: any = ref('')
|
||||
let searchValue = ref('')
|
||||
let list: any = ref([])
|
||||
let prop = defineProps({
|
||||
type: String,
|
||||
initialName: String,
|
||||
selectName: String,
|
||||
selectValue: String,
|
||||
other: String
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
name.value = prop.selectName
|
||||
popupValue.value = prop.selectValue
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const showPicker = () => {
|
||||
searchValue.value = name.value === prop.initialName ? '' : prop.type === 'cus' ? name.value : popupValue.value
|
||||
list.value = name.value === prop.initialName ? [] : [{
|
||||
text: name.value,
|
||||
value: popupValue.value
|
||||
}]
|
||||
showPickerFlag.value = true
|
||||
}
|
||||
|
||||
import { stockStockInfoLookUp, cusInfoLookUp } from '@/utils/api'
|
||||
import { debounceThrottle } from '@/mixins/debounce-throttle'
|
||||
const { throttle } = debounceThrottle()
|
||||
const onSearchInput = throttle(
|
||||
async () => {
|
||||
if (searchValue.value) {
|
||||
if (prop.type === 'stock') {
|
||||
const { data } = await stockStockInfoLookUp({ stockCode: searchValue.value })
|
||||
list.value = data.map((ele: any) => {
|
||||
ele.text = `${ele.stockCode}【${ele.stockName}】`
|
||||
ele.value = ele.stockCode
|
||||
return ele
|
||||
})
|
||||
} else if (prop.type === 'cus') {
|
||||
const { data } = await cusInfoLookUp({ cusName: searchValue.value })
|
||||
list.value = data.map((ele: any) => {
|
||||
ele.text = ele.cusName
|
||||
ele.value = ele.cusId
|
||||
return ele
|
||||
})
|
||||
}
|
||||
} else {
|
||||
list.value = []
|
||||
}
|
||||
},
|
||||
500
|
||||
)
|
||||
|
||||
const clearName = () => {
|
||||
name.value = prop.initialName
|
||||
popupValue.value = ''
|
||||
emit('refresh', { value: popupValue.value, name: name.value })
|
||||
}
|
||||
|
||||
const onConfirm = (e: any) => {
|
||||
console.log(e)
|
||||
popupValue.value = e.selectedIndexes > -1 ? e.selectedOptions[0].value : ''
|
||||
name.value = e.selectedIndexes > -1 ? e.selectedOptions[0].text : prop.initialName
|
||||
showPickerFlag.value = false
|
||||
emit('refresh', { value: popupValue.value, name: name.value })
|
||||
}
|
||||
const emit = defineEmits(['refresh'])
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
31
src/main.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import './assets/iconfont/iconfont.css'
|
||||
import './assets/base.css'
|
||||
import './assets/index.scss'
|
||||
import 'vant/es/toast/style'
|
||||
import 'vant/es/dialog/style'
|
||||
import '@vant/touch-emulator'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import pinia from './stores/index'
|
||||
import wx from 'weixin-js-sdk'
|
||||
import { wxMpJsapiGetJsapiTicket } from '@/utils/api'
|
||||
import VueWechatTitle from 'vue-wechat-title'
|
||||
wxMpJsapiGetJsapiTicket({ url: window.location.href }).then(({ data }) => {
|
||||
console.log(data)
|
||||
wx.config({
|
||||
appId: 'wx1d52f91f259a691f',
|
||||
timestamp: data.timestamp,
|
||||
nonceStr: data.nonceStr,
|
||||
signature: data.signature,
|
||||
jsApiList: ['updateAppMessageShareData']
|
||||
})
|
||||
})
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
const app = createApp(App)
|
||||
app.use(VueWechatTitle)
|
||||
app.use(router)
|
||||
app.use(pinia)
|
||||
|
||||
//Vue3挂载全局API
|
||||
app.mount('#app')
|
||||
41
src/mixins/chart-mixins.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ref, onMounted, onBeforeUnmount, nextTick, type Ref } from 'vue'
|
||||
import type { EChartsType } from 'echarts'
|
||||
export function chartMixins() {
|
||||
const charts: Ref<(EChartsType | null)[]> = ref([])
|
||||
const destroyedFlag: Ref<boolean> = ref(false)
|
||||
|
||||
const disposeAll = () => {
|
||||
charts.value.map((chart) => {
|
||||
if (chart) {
|
||||
chart.dispose()
|
||||
chart = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const changeChartSize = () => {
|
||||
nextTick(() => {
|
||||
charts.value.forEach((chart) => {
|
||||
if (chart) {
|
||||
chart.resize()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', changeChartSize)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
destroyedFlag.value = true
|
||||
window.removeEventListener('resize', changeChartSize)
|
||||
disposeAll()
|
||||
})
|
||||
|
||||
return {
|
||||
charts,
|
||||
disposeAll,
|
||||
destroyedFlag,
|
||||
}
|
||||
}
|
||||
40
src/mixins/debounce-throttle.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
export function debounceThrottle() {
|
||||
// 防抖
|
||||
function debounce(fn: any, delay: number) {
|
||||
let timer: any = null
|
||||
return function () {
|
||||
// 保留调用时的this上下文
|
||||
const context = this
|
||||
// 保留调用时传入的参数
|
||||
const args: any = arguments
|
||||
// 每次事件被触发时,都去清除之前的旧定时器
|
||||
clearTimeout(timer)
|
||||
// 设立新定时器
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(context, args)
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
// 节流
|
||||
function throttle(fn: any, delay: number) {
|
||||
let timer: any = null
|
||||
return function () {
|
||||
// 保留调用时的this上下文
|
||||
const context = this
|
||||
// 保留调用时传入的参数
|
||||
const args: any = arguments
|
||||
if (timer) {
|
||||
return
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(context, args)
|
||||
timer = null
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
return {
|
||||
debounce,
|
||||
throttle
|
||||
}
|
||||
}
|
||||
65
src/mixins/list-load-and-refresh.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
import emitter from '@/utils/mitt'
|
||||
|
||||
export function listLoadAndRefresh(getData: any, type: string, scrollPage: any = {}) {
|
||||
const refreshing = ref(false)
|
||||
const finished = ref(false)
|
||||
const curPage = ref(1)
|
||||
const loading = ref(false)
|
||||
const list: any = ref([])
|
||||
const totalCount = ref(0)
|
||||
const firstFlag = ref(true)
|
||||
|
||||
async function onLoad() {
|
||||
if (firstFlag.value) {
|
||||
emitter.emit('showLoading', '')
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await getData(curPage.value)
|
||||
totalCount.value = data.totalCount
|
||||
if (refreshing.value) {
|
||||
list.value = data.list
|
||||
refreshing.value = false
|
||||
} else {
|
||||
list.value = list.value.concat(data.list)
|
||||
}
|
||||
if (list.value.length >= data.totalCount) {
|
||||
finished.value = true
|
||||
} else {
|
||||
curPage.value++
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
if (type === 'internal' || type === 'report') {
|
||||
loading.value = false
|
||||
scrollPage()
|
||||
} else {
|
||||
loading.value = false
|
||||
if (firstFlag.value) {
|
||||
emitter.emit('hiddenLoading', '')
|
||||
firstFlag.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onRefresh() {
|
||||
// list.value = []
|
||||
refreshing.value = true
|
||||
finished.value = false
|
||||
curPage.value = 1
|
||||
onLoad()
|
||||
}
|
||||
return {
|
||||
refreshing,
|
||||
finished,
|
||||
loading,
|
||||
list,
|
||||
onLoad,
|
||||
onRefresh,
|
||||
curPage,
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
104
src/mixins/resize-listener-and-swipe-indicator.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
|
||||
export const resizeListenerAndSwipeIndicator = (
|
||||
charts: any,
|
||||
getData: any
|
||||
) => {
|
||||
const horizontalFlag = ref(true)
|
||||
const active = ref(0)
|
||||
const swipe = ref()
|
||||
const horizontalDiv = ref()
|
||||
onMounted(() => {
|
||||
console.log(swipe.value);
|
||||
|
||||
if (horizontalDiv.value) {
|
||||
horizontalDiv.value.addEventListener('touchstart', handleTouchStart)
|
||||
horizontalDiv.value.addEventListener('touchmove', handleTouchMove)
|
||||
horizontalDiv.value.addEventListener('touchend', handleTouchEnd)
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
disposeAll()
|
||||
if (horizontalDiv.value) {
|
||||
horizontalDiv.value.removeEventListener('touchstart', handleTouchStart)
|
||||
horizontalDiv.value.removeEventListener('touchmove', handleTouchMove)
|
||||
horizontalDiv.value.removeEventListener('touchend', handleTouchEnd)
|
||||
}
|
||||
})
|
||||
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let isSwiping = false
|
||||
|
||||
const handleTouchStart = (event: TouchEvent) => {
|
||||
if (horizontalFlag.value) return
|
||||
const touch = event.touches[0]
|
||||
startX = touch.pageX
|
||||
startY = touch.pageY
|
||||
isSwiping = true
|
||||
}
|
||||
|
||||
const handleTouchMove = (event: TouchEvent) => {
|
||||
if (!isSwiping || horizontalFlag.value) return
|
||||
const touch = event.touches[0]
|
||||
const deltaX = touch.pageX - startX
|
||||
const deltaY = touch.pageY - startY
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
if (deltaX > 0) {
|
||||
next()
|
||||
} else if (deltaX < 0) {
|
||||
pre()
|
||||
}
|
||||
isSwiping = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
if (horizontalFlag.value) {
|
||||
isSwiping = false
|
||||
}
|
||||
}
|
||||
|
||||
const disposeAll = () => {
|
||||
charts.value.forEach((chart: any) => {
|
||||
chart && chart.dispose()
|
||||
chart = null
|
||||
})
|
||||
}
|
||||
const onChange = (e: number) => {
|
||||
active.value = e
|
||||
const chart = charts.value[e]
|
||||
chart && chart.dispose()
|
||||
getData(active.value)
|
||||
}
|
||||
const showHorizontalChart = () => {
|
||||
horizontalFlag.value = !horizontalFlag.value
|
||||
if (!swipe.value) {
|
||||
nextTick(() => {
|
||||
charts.value[active.value].resize()
|
||||
})
|
||||
} else {
|
||||
disposeAll()
|
||||
swipe.value && swipe.value.resize()
|
||||
getData(active.value)
|
||||
}
|
||||
}
|
||||
const next = () => {
|
||||
swipe.value.next()
|
||||
}
|
||||
const pre = () => {
|
||||
swipe.value.prev()
|
||||
}
|
||||
return {
|
||||
onChange,
|
||||
active,
|
||||
next,
|
||||
pre,
|
||||
swipe,
|
||||
disposeAll,
|
||||
showHorizontalChart,
|
||||
horizontalFlag,
|
||||
horizontalDiv
|
||||
}
|
||||
}
|
||||
59
src/mixins/scroll-list.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ref, onActivated, nextTick } from 'vue'
|
||||
import listRefreshInfoStore from '@/stores/listRefreshInfo'
|
||||
const listRefreshInfo = listRefreshInfoStore()
|
||||
|
||||
export function scrollList(pageContent: any, scrollPosition: any, refresh: any) {
|
||||
const refreshFlag = ref(true)
|
||||
function setScrollTop() {
|
||||
console.log(1)
|
||||
onActivated(() => {
|
||||
nextTick(() => {
|
||||
if (listRefreshInfo.refreshId) {
|
||||
refresh('refresh', listRefreshInfo.refreshId)
|
||||
listRefreshInfo.refreshId = ''
|
||||
} else if (listRefreshInfo.deleteId) {
|
||||
refresh('delete', listRefreshInfo.deleteId)
|
||||
listRefreshInfo.deleteId = ''
|
||||
} else if (listRefreshInfo.addId) {
|
||||
listRefreshInfo.addId = ''
|
||||
refresh()
|
||||
} else {
|
||||
if (scrollPosition.value && pageContent.value) {
|
||||
pageContent.value.scrollTop = scrollPosition.value
|
||||
}
|
||||
if (refreshFlag.value === true) {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function setScrollPositionAndRefreshFlag(to: any, page: string) {
|
||||
if (pageContent.value) {
|
||||
scrollPosition.value = pageContent.value.scrollTop
|
||||
}
|
||||
if (page.includes(',')) {
|
||||
const pages = page.split(',')
|
||||
const index = pages.findIndex((ele: any) => {
|
||||
return to.name === ele
|
||||
})
|
||||
if (index > -1) {
|
||||
refreshFlag.value = false
|
||||
} else {
|
||||
refreshFlag.value = true
|
||||
}
|
||||
} else {
|
||||
if (to.name === page) {
|
||||
refreshFlag.value = false
|
||||
} else {
|
||||
refreshFlag.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setScrollTop,
|
||||
setScrollPositionAndRefreshFlag
|
||||
}
|
||||
}
|
||||
14
src/mixins/show-file.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export function showFile() {
|
||||
function loadingFile(url: any) {
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
link.href = url
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
return {
|
||||
loadingFile
|
||||
}
|
||||
}
|
||||
48
src/mixins/tap-mixins.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
export function tapMixins() {
|
||||
const tappedItem = ref<string | null>(null)
|
||||
const isProcessing = ref(false)
|
||||
const router = useRouter()
|
||||
|
||||
const onTouchStart = (name: string) => {
|
||||
if (isProcessing.value) return
|
||||
tappedItem.value = name
|
||||
}
|
||||
|
||||
const onTouchEnd = (name: string, url: string, id?: number, delay: number = 150) => {
|
||||
if (isProcessing.value || tappedItem.value !== name) return
|
||||
isProcessing.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
tappedItem.value = null
|
||||
setTimeout(() => {
|
||||
if (url) {
|
||||
if (id) {
|
||||
router.push({ name: url, params: { id } })
|
||||
} else {
|
||||
router.push({ name: url })
|
||||
}
|
||||
} else {
|
||||
showToast('正在开发中')
|
||||
}
|
||||
isProcessing.value = false
|
||||
}, 50)
|
||||
}, delay)
|
||||
}
|
||||
|
||||
const onTouchCancel = (name: string) => {
|
||||
if (tappedItem.value === name) {
|
||||
tappedItem.value = null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tappedItem,
|
||||
isProcessing,
|
||||
onTouchStart,
|
||||
onTouchEnd,
|
||||
onTouchCancel
|
||||
}
|
||||
}
|
||||
246
src/router/index.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/404',
|
||||
name: '404',
|
||||
component: () => import('../views/common/error.vue'),
|
||||
meta: { title: '404未找到', isPass: true }
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: () => import('../views/common/login.vue'),
|
||||
meta: { title: '登录', isPass: true }
|
||||
},
|
||||
{
|
||||
path: '/loading-page',
|
||||
name: 'loading-page',
|
||||
component: () => import('../views/common/loading-page.vue'),
|
||||
meta: { title: '加载', isPass: true }
|
||||
},
|
||||
{
|
||||
path: '/organization-list',
|
||||
name: 'organization-list',
|
||||
component: () => import('../views/organization/list.vue'),
|
||||
meta: { title: '机构列表', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/organization-detail',
|
||||
name: 'organization-detail',
|
||||
component: () => import('../views/organization/detail.vue'),
|
||||
meta: { title: '机构详情' }
|
||||
},
|
||||
{
|
||||
path: '/comment-list',
|
||||
name: 'comment-list',
|
||||
component: () => import('../views/comment/list.vue'),
|
||||
meta: { title: '点评列表', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/comment-detail',
|
||||
name: 'comment-detail',
|
||||
component: () => import('../views/comment/detail.vue'),
|
||||
meta: { title: '点评详情' }
|
||||
},
|
||||
{
|
||||
path: '/meeting-list',
|
||||
name: 'meeting-list',
|
||||
component: () => import('../views/meeting/list.vue'),
|
||||
meta: { title: '会议列表', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/meeting-detail',
|
||||
name: 'meeting-detail',
|
||||
component: () => import('../views/meeting/detail.vue'),
|
||||
meta: { title: '会议详情' }
|
||||
},
|
||||
{
|
||||
path: '/add-or-update-meeting',
|
||||
name: 'add-or-update-meeting',
|
||||
component: () => import('../views/meeting/add-or-update-meeting.vue'),
|
||||
meta: { title: '会议' }
|
||||
},
|
||||
{
|
||||
path: '/communicate-list',
|
||||
name: 'communicate-list',
|
||||
component: () => import('../views/communicate/list.vue'),
|
||||
meta: { title: '沟通列表', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/communicate-detail',
|
||||
name: 'communicate-detail',
|
||||
component: () => import('../views/communicate/detail.vue'),
|
||||
meta: { title: '沟通详情' }
|
||||
},
|
||||
{
|
||||
path: '/add-cus',
|
||||
name: 'add-cus',
|
||||
component: () => import('../views/communicate/add-cus.vue'),
|
||||
meta: { title: '添加机构' }
|
||||
},
|
||||
{
|
||||
path: '/add-cusUser',
|
||||
name: 'add-cusUser',
|
||||
component: () => import('../views/communicate/add-cusUser.vue'),
|
||||
meta: { title: '添加联系人' }
|
||||
},
|
||||
{
|
||||
path: '/report-list',
|
||||
name: 'report-list',
|
||||
component: () => import('../views/report/list.vue'),
|
||||
meta: { title: '报告列表' }
|
||||
},
|
||||
{
|
||||
path: '/stock-list',
|
||||
name: 'stock-list',
|
||||
component: () => import('../views/stock/list.vue'),
|
||||
meta: { title: '股票动态', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/stock-detail',
|
||||
name: 'stock-detail',
|
||||
component: () => import('../views/stock/detail.vue'),
|
||||
meta: { title: '股票详情', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/target-table',
|
||||
name: 'target-table',
|
||||
component: () => import('../views/target/table.vue'),
|
||||
meta: { title: '重要指标', keepAlive: true, isPass: true }
|
||||
},
|
||||
{
|
||||
path: '/economy-list',
|
||||
name: 'economy-list',
|
||||
component: () => import('../views/target/economy/economy-list.vue'),
|
||||
meta: { title: '经济专题', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/economy-detail/:id?',
|
||||
name: 'economy-detail',
|
||||
component: () => import('../views/target/economy/economy-detail.vue'),
|
||||
meta: { title: '专题详情' }
|
||||
},
|
||||
{
|
||||
path: '/fred-list',
|
||||
name: 'fred-list',
|
||||
component: () => import('../views/target/fred/fred-list.vue'),
|
||||
meta: { title: 'FRED专题', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/fred-detail/:id?',
|
||||
name: 'fred-detail',
|
||||
component: () => import('../views/target/fred/fred-detail.vue'),
|
||||
meta: { title: 'FRED详情' }
|
||||
},
|
||||
{
|
||||
path: '/series-list',
|
||||
name: 'series-list',
|
||||
component: () => import('../views/target/series/series-list.vue'),
|
||||
meta: { title: '指数系列', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/index-list/:id?',
|
||||
name: 'index-list',
|
||||
component: () => import('../views/target/index/index-list.vue'),
|
||||
meta: { title: '指数专题', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/index-detail/:id?',
|
||||
name: 'index-detail',
|
||||
component: () => import('../views/target/index/index-detail.vue'),
|
||||
meta: { title: '指数详情', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/etf-detail/:id?',
|
||||
name: 'etf-detail',
|
||||
component: () => import('../views/target/etf/etf-detail.vue'),
|
||||
meta: { title: 'ETF详情' }
|
||||
},
|
||||
{
|
||||
path: '/fund-list',
|
||||
name: 'fund-list',
|
||||
component: () => import('../views/target/fund/fund-list.vue'),
|
||||
meta: { title: '基金列表', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/fund-detail/:id?',
|
||||
name: 'fund-detail',
|
||||
component: () => import('../views/target/fund/fund-detail.vue'),
|
||||
meta: { title: '基金详情' }
|
||||
},
|
||||
{
|
||||
path: '/group-list',
|
||||
name: 'group-list',
|
||||
component: () => import('../views/target/group/group-list.vue'),
|
||||
meta: { title: '精选组合', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/group-detail/:id?',
|
||||
name: 'group-detail',
|
||||
component: () => import('../views/target/group/group-detail.vue'),
|
||||
meta: { title: '组合详情' }
|
||||
},
|
||||
{
|
||||
path: '/internal-meeting',
|
||||
name: 'internal-meeting',
|
||||
component: () => import('../views/internal/meeting.vue'),
|
||||
meta: { title: '内部会议' }
|
||||
},
|
||||
{
|
||||
path: '/subscription',
|
||||
name: 'subscription',
|
||||
component: () => import('../views/common/subscription.vue'),
|
||||
meta: { title: '订阅' }
|
||||
},
|
||||
{
|
||||
path: '/flow-company',
|
||||
name: 'flow-company',
|
||||
component: () => import('../views/flow/company.vue'),
|
||||
meta: { title: '重点公司', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/flow',
|
||||
name: 'flow',
|
||||
component: () => import('../views/flow/flow.vue'),
|
||||
meta: { title: '重点公司追踪', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/flow-detail',
|
||||
name: 'flow-detail',
|
||||
component: () => import('../views/flow/flow-detail.vue'),
|
||||
meta: { title: '重点公司追踪详情' }
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
name: 'user',
|
||||
component: () => import('../views/user/index.vue'),
|
||||
meta: { title: '个人中心', isPass: true }
|
||||
},
|
||||
{
|
||||
path: '/log',
|
||||
name: 'log',
|
||||
component: () => import('../views/common/log.vue'),
|
||||
meta: { title: '日志详情', isPass: true }
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('../views/home/index.vue'),
|
||||
meta: { title: '首页', isPass: true }
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const userInfo = userInfoStore()
|
||||
if (!to.meta.isPass && !userInfo.token ) {
|
||||
return next({ name: 'login' })
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
17
src/stores/etfStockInfo.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('etfStockInfo', {
|
||||
state: () => ({
|
||||
stockCode: '',
|
||||
companyList: <any>[],
|
||||
xAxisData: []
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'etfStockInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
15
src/stores/fileInfo.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('fileInfo', {
|
||||
state: () => ({
|
||||
fileId: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'fileInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
38
src/stores/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createPinia } from 'pinia'
|
||||
import persistedState from 'pinia-plugin-persistedstate'
|
||||
import SecureLS from 'secure-ls'
|
||||
export const ls = new SecureLS({
|
||||
encodingType: 'aes',
|
||||
isCompression: false
|
||||
})
|
||||
// 创建
|
||||
const pinia = createPinia()
|
||||
pinia.use(persistedState)
|
||||
pinia.use(({ store }) => {
|
||||
// 加密状态并存储到 SecureLS 中
|
||||
const encryptAndStoreState = () => {
|
||||
ls.set(store.$id, store.$state)
|
||||
}
|
||||
|
||||
// 解密状态并恢复到 Pinia 中
|
||||
const decryptAndRestoreState = () => {
|
||||
try {
|
||||
const data = ls.get(store.$id)
|
||||
if (data) {
|
||||
store.$state = data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error decrypting and restoring state:', error)
|
||||
// 处理异常情况,比如重新初始化状态或显示错误信息
|
||||
store.$reset()
|
||||
}
|
||||
}
|
||||
|
||||
// 在每次状态变更时调用加密函数
|
||||
store.$subscribe(encryptAndStoreState)
|
||||
|
||||
// 在初始化时调用解密函数
|
||||
decryptAndRestoreState()
|
||||
})
|
||||
// 导出
|
||||
export default pinia
|
||||
17
src/stores/listRefreshInfo.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('fileInfo', {
|
||||
state: () => ({
|
||||
refreshId: '',
|
||||
deleteId: '',
|
||||
addId: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'fileInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
21
src/stores/reportInfo.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('speakInfo', {
|
||||
state: () => ({
|
||||
reportId: '',
|
||||
index: 0,
|
||||
stockCode: '',
|
||||
stockName: '股票',
|
||||
date: '日期',
|
||||
reportTypeName: '类型',
|
||||
reportType: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'speakInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
45
src/stores/saveCommunicateInfo.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('saveCommunicateInfo', {
|
||||
state: () => ({
|
||||
linkTypeName: '',
|
||||
selectlinkType: '',
|
||||
dicNote2: '',
|
||||
dicNote3: '',
|
||||
sysDay: '',
|
||||
selectsysDay: [] as string[],
|
||||
sysTime: '',
|
||||
selectsysTime: [] as string[],
|
||||
cusName: '',
|
||||
selectcus: '',
|
||||
cusLevelName: '',
|
||||
selectcusLevel: '',
|
||||
linkTitle: '',
|
||||
cusUserNames: '',
|
||||
selectcusUserIds: [],
|
||||
staffNames: '',
|
||||
selectstaffIds: [],
|
||||
saleNames: '',
|
||||
selectsaleIds: [],
|
||||
linkFromName: '',
|
||||
linkFrom: 1,
|
||||
linkWayName: '',
|
||||
selectlinkWay: '',
|
||||
linkNote: '',
|
||||
meetingName: '',
|
||||
selectmeeting: '',
|
||||
staffNote: '',
|
||||
cusLinkStockList: <any>[],
|
||||
cusLinkStockListForLinkId: <any>[],
|
||||
saleScore: 0,
|
||||
saleNote: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'saveCommunicateInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
45
src/stores/saveCusInfo.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('saveCusInfo', {
|
||||
state: () => ({
|
||||
cusName: '',
|
||||
cusLevelName: '',
|
||||
selectcusLevel: '',
|
||||
cusUserName: '',
|
||||
selectcusUser: '',
|
||||
companyTypeName: '',
|
||||
selectcompanyType: '',
|
||||
cityName: '',
|
||||
selectcity: '',
|
||||
address: '',
|
||||
accountStatus: '1',
|
||||
accountTime: '',
|
||||
selectaccountTime: [] as string[],
|
||||
accountTypeNames: '',
|
||||
selectaccountTypeIds: [],
|
||||
isMoney: '0',
|
||||
tradeMoney: '',
|
||||
capitalScaleName: '',
|
||||
selectcapitalScale: '',
|
||||
capitalScaleValue: '',
|
||||
fundNum: '',
|
||||
investOverseasPer: '',
|
||||
investResearchNum: '',
|
||||
investChannelName: '',
|
||||
selectinvestChannel: '',
|
||||
website: '',
|
||||
companyCreateTime: '',
|
||||
selectcompanyCreateTime: [] as string[],
|
||||
introduction: '',
|
||||
saleUserNames: '',
|
||||
selectsaleUserIds: []
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'saveCusInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
19
src/stores/saveInfo.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('speakInfo', {
|
||||
state: () => ({
|
||||
id: '',
|
||||
name: 0,
|
||||
type: '',
|
||||
positionName: '',
|
||||
day: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'speakInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
18
src/stores/speakInfo.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('speakInfo', {
|
||||
state: () => ({
|
||||
speakId: '',
|
||||
index: 0,
|
||||
speakType: '',
|
||||
speakTypeName: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'speakInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
15
src/stores/stockInfo.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('stockInfo', {
|
||||
state: () => ({
|
||||
id: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'stockInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
45
src/stores/updateCommunicateInfo.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('updateCommunicateInfo', {
|
||||
state: () => ({
|
||||
linkTypeName: '',
|
||||
selectlinkType: '',
|
||||
dicNote2: '',
|
||||
dicNote3: '',
|
||||
sysDay: '',
|
||||
selectsysDay: [] as string[],
|
||||
sysTime: '',
|
||||
selectsysTime: [] as string[],
|
||||
cusName: '',
|
||||
selectcus: '',
|
||||
cusLevelName: '',
|
||||
selectcusLevel: '',
|
||||
linkTitle: '',
|
||||
cusUserNames: '',
|
||||
selectcusUserIds: [],
|
||||
staffNames: '',
|
||||
selectstaffIds: [],
|
||||
saleNames: '',
|
||||
selectsaleIds: [],
|
||||
linkFromName: '',
|
||||
linkFrom: 1,
|
||||
linkWayName: '',
|
||||
selectlinkWay: '',
|
||||
linkNote: '',
|
||||
meetingName: '',
|
||||
selectmeeting: '',
|
||||
staffNote: '',
|
||||
cusLinkStockList: <any>[],
|
||||
cusLinkStockListForLinkId: <any>[],
|
||||
saleScore: 0,
|
||||
saleNote: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'updateCommunicateInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
22
src/stores/userInfo.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { defineStore } from 'pinia'
|
||||
export default defineStore('userInfo', {
|
||||
state: () => ({
|
||||
permissions: [] as string[],
|
||||
token: '',
|
||||
id: '',
|
||||
username: '',
|
||||
password: '',
|
||||
roleId: '',
|
||||
realname: '',
|
||||
headPic: ''
|
||||
}),
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
key: 'userInfo', // 持久化状态的键
|
||||
storage: localStorage // 使用localStorage来持久化状态
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
1
src/types/vue-virtual-scroller.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module 'vue-virtual-scroller';
|
||||
818
src/utils/api.ts
Normal file
@@ -0,0 +1,818 @@
|
||||
import http from './request'
|
||||
|
||||
/** 系统 **/
|
||||
// 登录微信公众号
|
||||
export function wxMpJsapiGetJsapiTicket(params: object) {
|
||||
return http.request({
|
||||
url: '/wx/mp/jsapi/getJsapiTicket',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function sysLog(id: number) {
|
||||
return http.request({
|
||||
url: `/sys/user/log/app/info/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
// 登录
|
||||
export function sysLogin(data: object) {
|
||||
return http.request({
|
||||
url: '/sys/login',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function sysMenuNav() {
|
||||
return http.request({
|
||||
url: '/sys/menu/nav',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
export function sysUserInfo() {
|
||||
return http.request({
|
||||
url: '/sys/user/info',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取研究员和管理层
|
||||
export function sysUserGetStaffAndManagerList() {
|
||||
return http.request({
|
||||
url: '/sys/user/getStaffAndManagerList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取销售和管理层
|
||||
export function sysUserGetSaleAndManagerList() {
|
||||
return http.request({
|
||||
url: '/sys/user/getSaleAndManagerList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 员工列表
|
||||
export function workStaffLookup() {
|
||||
return http.request({
|
||||
url: '/work/staff/lookup',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取全公司
|
||||
export function sysUserGetCompanyUserList() {
|
||||
return http.request({
|
||||
url: '/sys/user/getCompanyUserList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据类型获取字典
|
||||
export function sysDicListByType(params: object) {
|
||||
return http.request({
|
||||
url: '/sys/dic/listByType',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 根据类型和note获取字典
|
||||
export function sysDicListByNote(params: object) {
|
||||
return http.request({
|
||||
url: '/sys/dic/listByNote',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
/** 点评 **/
|
||||
// 点评列表
|
||||
export function workInboxList(params: object) {
|
||||
return http.request({
|
||||
url: '/work/inbox/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 点评详情
|
||||
export function workInboxInfo(inboxId: string) {
|
||||
return http.request({
|
||||
url: `/work/inbox/info/${inboxId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 会议 **/
|
||||
// 会议列表
|
||||
export function workMeetingList(query: string) {
|
||||
return http.request({
|
||||
url: `/work/meeting/list?${query}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function workMeetingUpdateOrSave(type: string, data: object) {
|
||||
return http.request({
|
||||
url: `/work/meeting/${type ? 'update' : 'save'}`,
|
||||
method: type ? 'put' : 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 会议详情
|
||||
export function workMeetingInfo(meetingId: string) {
|
||||
return http.request({
|
||||
url: `/work/meeting/info/${meetingId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除会议
|
||||
export function workMeetingDelete(meetingId: string) {
|
||||
return http.request(
|
||||
{
|
||||
url: '/work/meeting/delete',
|
||||
method: 'delete',
|
||||
data: [meetingId]
|
||||
},
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 会议可见人员
|
||||
export function workMeetingUserList(params: object) {
|
||||
return http.request({
|
||||
url: '/work/meeting/user/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 通过会议id获取会议资料
|
||||
export function workMeetingGetFileId(params: object) {
|
||||
return http.request({
|
||||
url: '/work/meeting/getFileId',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 通过会议id获取参会资料
|
||||
export function workMeetinGetUserFileMenuId(params: object) {
|
||||
return http.request({
|
||||
url: '/work/meeting/getUserFileMenuId',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 反查会议
|
||||
export function workMeetingLookUp(params: object) {
|
||||
return http.request({
|
||||
url: '/work/meeting/lookup',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 参会人员
|
||||
export function cusUserMeetingLogList(params: object) {
|
||||
return http.request({
|
||||
url: '/cus/usermeetinglog/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 删除参会人员
|
||||
export function cusUserMeetingLogDelete(data: object) {
|
||||
return http.request(
|
||||
{
|
||||
url: '/cus/usermeetinglog/delete',
|
||||
method: 'delete',
|
||||
data: data
|
||||
},
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 修改或新增参会人员
|
||||
export function cusUserMeetingLogUpdateOrSave(type: string, data: object) {
|
||||
return http.request(
|
||||
{
|
||||
url: `/cus/usermeetinglog/${type ? 'update' : 'save'}`,
|
||||
method: type ? 'put' : 'post',
|
||||
data: type ? data : [data]
|
||||
},
|
||||
!!type
|
||||
)
|
||||
}
|
||||
|
||||
// 获取文件列表
|
||||
export function workFileListByMenuId(params: object) {
|
||||
return http.request({
|
||||
url: '/work/file/listByMenuId',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
/** 报告 **/
|
||||
// 报告列表
|
||||
export function stockReportList(query: string, params: object) {
|
||||
return http.request({
|
||||
url: `/stock/report/list${query}`,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
/** 沟通 **/
|
||||
// 沟通列表
|
||||
export async function cusLinkList(query: string) {
|
||||
const { data } = await http.request({
|
||||
url: `/cus/link/list?${query}`,
|
||||
method: 'get'
|
||||
})
|
||||
return data.list.map((ele: any) => {
|
||||
ele.sysDay = ele.sysDate.slice(0, 10)
|
||||
switch (ele.linkType) {
|
||||
case '32':
|
||||
ele.tagColor = 'danger-background-color'
|
||||
break
|
||||
case '34':
|
||||
ele.tagColor = 'primary-background-color'
|
||||
break
|
||||
case '31':
|
||||
ele.tagColor = 'success-background-color'
|
||||
break
|
||||
}
|
||||
return ele
|
||||
})
|
||||
}
|
||||
|
||||
// 沟通详情
|
||||
export function cusLinkInfo(linkId: string) {
|
||||
return http.request({
|
||||
url: `/cus/link/info/${linkId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除沟通
|
||||
export function cusLinkDeleteByOne(params: object) {
|
||||
return http.request({
|
||||
url: '/cus/link/deleteByOne',
|
||||
method: 'delete',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 修改新增沟通
|
||||
export function cusLinkUpdateOrSave(type: string, data: object) {
|
||||
return http.request({
|
||||
url: `/cus/link/${type ? 'update' : 'save'}`,
|
||||
method: type ? 'put' : 'post',
|
||||
data: type ? data : { cusLinkEntityList: [data] }
|
||||
})
|
||||
}
|
||||
|
||||
// 反查机构
|
||||
export function cusInfoLookUp(params: object) {
|
||||
return http.request({
|
||||
url: '/cus/info/lookup',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 新增机构
|
||||
export function cusInfoSave(data: object) {
|
||||
return http.request({
|
||||
url: '/cus/info/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 机构列表
|
||||
export function cusInfoList(params: object) {
|
||||
return http.request({
|
||||
url: 'cus/info/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function cusInfoInfo(id: number) {
|
||||
return http.request({
|
||||
url: `cus/info/info/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function cusServiceGetByCusId (params: object) {
|
||||
return http.request({
|
||||
url: '/cus/service/getByCusId',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function cusYearList (params: object) {
|
||||
return http.request({
|
||||
url: '/cus/year/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function cusAccountList (params: object) {
|
||||
return http.request({
|
||||
url: '/cus/account/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function cusMeetingList (params: object) {
|
||||
return http.request({
|
||||
url: '/cus/usermeetinglog/listByCusId',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function cusInfoCountByMonth (params: object) {
|
||||
return http.request({
|
||||
url: '/cus/info/countByMonth',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 交易详情
|
||||
export function stockCusTradeMyTrade () {
|
||||
return http.request({
|
||||
url: '/stock/cus/trade/myTrade',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 反查联系人
|
||||
export function cusUserLookUp(params: object) {
|
||||
return http.request({
|
||||
url: '/cus/user/lookup',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function cusUserCusPositionList() {
|
||||
return http.request({
|
||||
url: '/cus/user/cusPositionList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function cusUserSave(data: object) {
|
||||
return http.request({
|
||||
url: '/cus/user/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 通过机构获取联系人
|
||||
export function cusUserListByCusId(params: object) {
|
||||
return http.request({
|
||||
url: '/cus/user/listByCusId',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
/* 股票 */
|
||||
// 股票列表
|
||||
export function stockStockinfoEventNews(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/stockinfo/event/news',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockStockinfoEventList(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/stockinfo/event/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 反查股票
|
||||
export function stockStockInfoLookUp(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/stockinfo/lookup',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthDepositVsM2() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/depositVsM2',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthMarketHSVsDeposit() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/marketHSVsDeposit',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthMarketHSJVsDeposit() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/marketHSJVsDeposit',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthListByItem(params: object) {
|
||||
return http.request({
|
||||
url: '/report/economy/month/listByItem',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockStockrzrqQueryRzrq() {
|
||||
return http.request({
|
||||
url: '/stock/stockrzrq/queryRzrq',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function stockStockrzrqQueryRzToTotalMarket() {
|
||||
return http.request({
|
||||
url: '/stock/stockrzrq/queryRzToTotalMarket',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function stockStockrzrqQueryRzjme(params: object) {
|
||||
return http.request({
|
||||
url: 'stock/stockrzrq/queryRzjme',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthStockShares() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/stockShares',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthStockMarket() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/stockMarket',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthStockAmount() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/stockAmount',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthStockTurnoverRate() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/stockTurnoverRate',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthStockVolume() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/stockVolume',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function reportEconomyMonthNewAccountNum() {
|
||||
return http.request({
|
||||
url: '/report/economy/month/newAccountNum',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function stockStockHsList() {
|
||||
return http.request({
|
||||
url: '/stock/stockhs/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function fredInfoList() {
|
||||
return http.request({
|
||||
url: '/fred/info/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function fredInfoInfo(id: string) {
|
||||
return http.request({
|
||||
url: `/fred/info/info/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function fredDetailQueryAaaAndBbb(params: object) {
|
||||
return http.request({
|
||||
url: '/fred/detail/query/AaaAndBbb',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function fredDetailQueryFred(params: object) {
|
||||
return http.request({
|
||||
url: '/fred/detail/queryFred',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const indexInfoSeries = (params: object) => {
|
||||
return http.request({
|
||||
url: '/index/info/series',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const indexInfo = (id: string) => {
|
||||
return http.request({
|
||||
url: `/index/info/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const indexInfoIndexHistChart = (id: string) => {
|
||||
return http.request({
|
||||
url: `/index/info/indexHistChart/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundHisQueryFundHis(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/his/queryFundHis',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const indexInfoIndustry1Chart = (id: string) => {
|
||||
return http.request({
|
||||
url: `/index/info/industry1Chart/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const indexInfoIndustry2Chart = (id: string) => {
|
||||
return http.request({
|
||||
url: `/index/info/industry2Chart/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const indexInfoStockGetChart = (params: object) => {
|
||||
return http.request({
|
||||
url: '/index/info/stock/getChart',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundList(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundGetByEtfCode(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/getByEtfCode',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundHisQueryEtfShareHis(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/his/queryEtfShareHis',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundDetailDayCoinList(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/detail/day/coinList',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundDetailDayList(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/detail/day/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundDetailQuarterCircleByExchange(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/detail/quarter/circleByExchange',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundDetailQuarterHisByExchange(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/detail/quarter/hisByExchange',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function indexFundDetailQuarterGetChart(params: object) {
|
||||
return http.request({
|
||||
url: '/index/fund/detail/quarter/getChart',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockAFundList(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/a/fund/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const stockAFundInfo = (id: string) => {
|
||||
return http.request({
|
||||
url: `/stock/a/fund/info/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function stockAFundHisFundHisChart(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/a/fund/his/fundHis/chart',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockAFundFileList(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/a/fund/file/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockAFundHisFundHisList(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/a/fund/his/fundHis/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockAFundListFund() {
|
||||
return http.request({
|
||||
url: 'stock/a/fund/listFund',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const groupList = (params: string) => {
|
||||
return http.request({
|
||||
url: `/group/list?${params}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function groupInfo(id: string) {
|
||||
return http.request({
|
||||
url: `/group/info/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function groupHisChartIncomeRate(params: object) {
|
||||
return http.request({
|
||||
url: '/group/his/chart/incomeRate',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function groupHoldList (params: object) {
|
||||
return http.request({
|
||||
url: '/group/holdList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function groupTradeList(params: object) {
|
||||
return http.request({
|
||||
url: '/group/trade/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockEtfDetailLookup(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/etf/detail/lookup',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 内部会议
|
||||
export function workSpeakList(params: object) {
|
||||
return http.request({
|
||||
url: '/work/speak/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 获取订阅信息
|
||||
export function getSubscriptionList() {
|
||||
return http.request({
|
||||
url: '/stock/subscription/config/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function delSubscription(id: number) {
|
||||
return http.request({
|
||||
url: '/stock/subscription/config/delete',
|
||||
method: 'delete',
|
||||
params: {
|
||||
id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function saveSubscription(subscriptionItem: string) {
|
||||
return http.request({
|
||||
url: '/stock/subscription/config/save',
|
||||
method: 'post',
|
||||
data: {
|
||||
subscriptionItem
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 重点公司追踪
|
||||
export function stockCompanyFlowInfoListCompany() {
|
||||
return http.request({
|
||||
url: '/stock/company/follow/info/listCompany',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function stockCompanyFlowInfoList(params: object) {
|
||||
return http.request({
|
||||
url: '/stock/company/follow/info/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function stockCompanyFlowInfoInfo(id: number) {
|
||||
return http.request({
|
||||
url: `/stock/company/follow/info/info/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
180
src/utils/chart.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
|
||||
|
||||
export function getXAxis(data: any) {
|
||||
const xAxis: any = {
|
||||
type: data.type || 'category',
|
||||
name: data.name || '',
|
||||
data: data.data || [],
|
||||
show: data.showFlag !== false,
|
||||
axisLabel: {
|
||||
show: data.showFlag !== false,
|
||||
showMaxLabel: true,
|
||||
interval: 'auto',
|
||||
rotate: data.rotate || 0,
|
||||
formatter: data.axisLabel || '{value}',
|
||||
},
|
||||
gridIndex: data.gridIndex || 0,
|
||||
}
|
||||
if (data.max !== null && data.max !== undefined) xAxis.max = data.max
|
||||
if (data.min !== null && data.min !== undefined) xAxis.min = data.min
|
||||
if (data.maxInterval !== undefined) xAxis.maxInterval = data.maxInterval
|
||||
if (data.minInterval !== undefined) xAxis.minInterval = data.minInterval
|
||||
return xAxis
|
||||
}
|
||||
|
||||
export function getYAxis(data: any) {
|
||||
const yAxis: any = {
|
||||
type: data.type || 'value',
|
||||
name: data.name || '',
|
||||
nameLocation: data.nameLocation || 'end',
|
||||
scale: true,
|
||||
show: data.showFlag !== false,
|
||||
axisLabel: {
|
||||
show: data.showFlag !== false,
|
||||
formatter: data.axisLabel || '{value}',
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: data.color || '#000',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
position: data.position || 'left',
|
||||
gridIndex: data.gridIndex || 0,
|
||||
offset: data.offset || 0,
|
||||
}
|
||||
if (data.max !== null && data.max !== undefined) yAxis.max = data.max
|
||||
if (data.min !== null && data.min !== undefined) yAxis.min = data.min
|
||||
if (data.maxInterval !== undefined) yAxis.maxInterval = data.maxInterval
|
||||
if (data.minInterval !== undefined) yAxis.minInterval = data.minInterval
|
||||
if (data.splitNumber !== undefined) yAxis.splitNumber = data.splitNumber
|
||||
return yAxis
|
||||
}
|
||||
|
||||
export function getLineSeries(data: any) {
|
||||
const series = {
|
||||
type: 'line',
|
||||
name: data.name || '',
|
||||
data: data.data || [],
|
||||
yAxisIndex: data.yAxisIndex || 0,
|
||||
xAxisIndex: data.xAxisIndex || 0,
|
||||
connectNulls: data.connectNulls || false,
|
||||
symbol: data.symbol || 'none',
|
||||
symbolSize: data.symbolSize || 0,
|
||||
lineStyle:
|
||||
data.color || data.lineType
|
||||
? {
|
||||
color: data.color,
|
||||
type: data.lineType,
|
||||
}
|
||||
: undefined,
|
||||
markPoint: data.markPoint,
|
||||
areaStyle: data.areaStyle,
|
||||
}
|
||||
return series
|
||||
}
|
||||
|
||||
export function getBarSeries (data: any) {
|
||||
const serise = {
|
||||
type: 'bar',
|
||||
name: data.name || '',
|
||||
data: data.data || [],
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
label: {
|
||||
show: data.labelFlag !== false,
|
||||
position: data.position || 'top'
|
||||
},
|
||||
yAxisIndex: data.yAxisIndex || 0,
|
||||
xAxisIndex: data.xAxisIndex || 0,
|
||||
stack: data.stack || '',
|
||||
showEmptyData: false
|
||||
}
|
||||
return serise
|
||||
}
|
||||
|
||||
|
||||
export function getBaseOption(data: any) {
|
||||
data.series.map((ele: any) => {
|
||||
if (ele.type !== 'line' || !ele?.data) return
|
||||
if (ele.data.length === 1) {
|
||||
ele.symbolSize = 4
|
||||
ele.symbol = 'circle'
|
||||
} else if (data.xAxis?.[0]?.type === 'category') {
|
||||
ele.data.forEach((res: any, index: number) => {
|
||||
if (typeof res !== 'object' || res === null || !('value' in res) || res.value !== null)
|
||||
return
|
||||
const prevPoint = ele.data?.[index - 1]
|
||||
if (
|
||||
index > 0 &&
|
||||
prevPoint &&
|
||||
typeof prevPoint === 'object' &&
|
||||
'value' in prevPoint &&
|
||||
prevPoint.value !== null
|
||||
) {
|
||||
prevPoint.symbolSize = 4
|
||||
prevPoint.symbol = 'circle'
|
||||
}
|
||||
const nextPoint = ele.data?.[index + 1]
|
||||
if (
|
||||
ele.data &&
|
||||
index < ele.data.length - 1 &&
|
||||
nextPoint &&
|
||||
typeof nextPoint === 'object' &&
|
||||
'value' in nextPoint &&
|
||||
nextPoint.value !== null
|
||||
) {
|
||||
nextPoint.symbolSize = 4
|
||||
nextPoint.symbol = 'circle'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
const option: any = {
|
||||
series: data.series,
|
||||
yAxis: Array.isArray(data.yAxis) ? data.yAxis : data.yAxis ? [data.yAxis] : undefined,
|
||||
xAxis: Array.isArray(data.xAxis) ? data.xAxis : data.xAxis ? [data.xAxis] : undefined,
|
||||
}
|
||||
if (data.tooltip) {
|
||||
option.tooltip = data.tooltip
|
||||
}
|
||||
if (data.tooltipFormatter) {
|
||||
option.tooltip = {
|
||||
...option.tooltip,
|
||||
trigger: 'axis',
|
||||
formatter: data.tooltipFormatter,
|
||||
}
|
||||
}
|
||||
if (data.visualMap) option.visualMap = data.visualMap
|
||||
if (data.toolbox) option.toolbox = data.toolbox
|
||||
if (data.grid) option.grid = data.grid
|
||||
if (data.legend) option.legend = data.legend
|
||||
if (data.color) option.color = data.color
|
||||
if (data.dataZoom) option.dataZoom = data.dataZoom
|
||||
if (data.title) {
|
||||
option.title = Array.isArray(data.title)
|
||||
? data.title.map(convertTitle)
|
||||
: [convertTitle(data.title)]
|
||||
}
|
||||
return option
|
||||
}
|
||||
|
||||
function convertTitle(title: any) {
|
||||
return {
|
||||
text: title.text,
|
||||
subtext: title.subtext,
|
||||
show: title.show ?? true,
|
||||
backgroundColor: title.backgroundColor || '#5FCFCB',
|
||||
textStyle: {
|
||||
color: title.textStyle?.color || '#fff',
|
||||
fontSize: title.textStyle?.fontSize || '18',
|
||||
...title.textStyle,
|
||||
},
|
||||
left: title.left || 'center',
|
||||
top: title.top || 0,
|
||||
}
|
||||
}
|
||||
15
src/utils/colorList.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// 公共颜色列表,用于统一各页面的颜色方案
|
||||
export const colorList = [
|
||||
{ color: '#165DFF', bgColor: '#E8F3FF' },
|
||||
{ color: '#FF7D00', bgColor: '#FFF7E8' },
|
||||
{ color: '#00B42A', bgColor: '#E8FFEF' },
|
||||
{ color: '#F53F3F', bgColor: '#FEE8E8' },
|
||||
{ color: '#A855F7', bgColor: '#F3E8FF' },
|
||||
{ color: '#EC4899', bgColor: '#FFE8EA' },
|
||||
{ color: '#0FC6C2', bgColor: '#EBFFFA' },
|
||||
{ color: '#722ED1', bgColor: '#F9F0FF' },
|
||||
{ color: '#FFC53D', bgColor: '#FFF9E8' },
|
||||
{ color: '#36CFC9', bgColor: '#E6FFFB' },
|
||||
{ color: '#72BCFF', bgColor: '#EDF5FF' },
|
||||
{ color: '#73D13D', bgColor: '#F0FFF4' }
|
||||
]
|
||||
241
src/utils/index.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
export function getWeek(week: any) {
|
||||
const weeks = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||
return weeks[week]
|
||||
}
|
||||
|
||||
export function deepCopy(obj: any) {
|
||||
const a = JSON.stringify(obj)
|
||||
const newobj = JSON.parse(a)
|
||||
return newobj
|
||||
}
|
||||
|
||||
export function getStockTwoArray(data: any, type: string) {
|
||||
const list: any = []
|
||||
data[0].forEach((ele: any, index: number) => {
|
||||
let res = {
|
||||
stockCode1: ele.stockCode,
|
||||
stockName1: ele.stockName,
|
||||
value1: `${ele[type]}${type === 'companyNum' ? '家' : ''}`,
|
||||
stockCode2: '',
|
||||
stockName2: '',
|
||||
value2: '',
|
||||
stockCode3: '',
|
||||
stockName3: '',
|
||||
value3: '',
|
||||
stockCode4: '',
|
||||
stockName4: '',
|
||||
value4: ''
|
||||
}
|
||||
res = getStockOneArray(res, data, 1, index, type)
|
||||
res = getStockOneArray(res, data, 2, index, type)
|
||||
res = getStockOneArray(res, data, 3, index, type)
|
||||
list.push(res)
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
export function getStockOneArray(res: any, data: any, len: number, index: number, type: string) {
|
||||
if (data.length > len && data[len].length > index) {
|
||||
res[`stockCode${len + 1}`] = data[len][index].stockCode
|
||||
res[`stockName${len + 1}`] = data[len][index].stockName
|
||||
res[`value${len + 1}`] = `${data[len][index][type]}${type === 'companyNum' ? '家' : ''}`
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
export function formatMoney(value: any) {
|
||||
const arr = (value + '').split('-')
|
||||
const negativeFlag = ''
|
||||
value = arr.length === 1 ? arr[0] : arr[1]
|
||||
if (value > 100000000) {
|
||||
return negativeFlag + Math.round(value / 1000000) / 100 + '亿'
|
||||
} else if (value > 10000) {
|
||||
return negativeFlag + Math.round(value / 100) / 100 + '万'
|
||||
} else {
|
||||
return negativeFlag + value
|
||||
}
|
||||
}
|
||||
|
||||
export function setDecimalPlaces(value: any, fixedNum: number, multiply = 100, divide = 100) {
|
||||
if (value) {
|
||||
return (Math.round(value * multiply) / divide).toFixed(fixedNum)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
import moment from 'moment'
|
||||
export function getEchartTime(dateValue: any) {
|
||||
let time = ''
|
||||
switch (dateValue) {
|
||||
case '1':
|
||||
time = moment().subtract(10, 'years').format('YYYY-MM-DD')
|
||||
break
|
||||
case '2':
|
||||
time = moment().subtract(1, 'years').format('YYYY-MM-DD')
|
||||
break
|
||||
case '3':
|
||||
time = moment().subtract(6, 'months').format('YYYY-MM-DD')
|
||||
break
|
||||
case '4':
|
||||
time = moment().subtract(3, 'months').format('YYYY-MM-DD')
|
||||
break
|
||||
case '5':
|
||||
time = moment().subtract(1, 'months').format('YYYY-MM-DD')
|
||||
break
|
||||
case '6':
|
||||
time = moment().subtract(1, 'weeks').format('YYYY-MM-DD')
|
||||
break
|
||||
}
|
||||
return time
|
||||
}
|
||||
|
||||
export function sortArr(arr: any) {
|
||||
return arr.sort((value1: any, value2: any) => {
|
||||
return Date.parse(value1) - Date.parse(value2)
|
||||
})
|
||||
}
|
||||
|
||||
export function convertToUrl(obj: any) {
|
||||
let url = ''
|
||||
for (const key in obj) {
|
||||
if (typeof obj[key] === 'number' || obj[key]) {
|
||||
url += `&${key}=${obj[key]}`
|
||||
}
|
||||
}
|
||||
return url ? url.slice(1) : ''
|
||||
}
|
||||
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
const userInfo = userInfoStore()
|
||||
export function isAuth(key: string) {
|
||||
return userInfo.permissions.indexOf(key) !== -1 || false
|
||||
}
|
||||
|
||||
import * as CryptoJS from 'crypto-js'
|
||||
export function encrypt(word: any) {
|
||||
const keyStr = 'firshshanghai!@#' // 判断是否存在ksy,不存在就用定义好的key
|
||||
const key = CryptoJS.enc.Utf8.parse(keyStr)
|
||||
const srcs = CryptoJS.enc.Utf8.parse(word)
|
||||
const encrypted = CryptoJS.AES.encrypt(srcs, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
})
|
||||
return encrypted.toString()
|
||||
}
|
||||
|
||||
export function addOrSubtractTime(date: string | Date, value: number, unit: 'year' | 'month') {
|
||||
const currentDate = typeof date === 'string' ? new Date(date) : date;
|
||||
const newDate = new Date(currentDate.getTime())
|
||||
if (unit === 'year') {
|
||||
newDate.setFullYear(newDate.getFullYear() + value)
|
||||
} else if (unit === 'month') {
|
||||
newDate.setMonth(newDate.getMonth() + value)
|
||||
}
|
||||
const year = newDate.getFullYear();
|
||||
const month = String(newDate.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(newDate.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
export function treeDataTranslate(data: any, id = 'id', pid = 'parentId') {
|
||||
const res: any = []
|
||||
const temp: any = {}
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
temp[data[i][id]] = data[i]
|
||||
}
|
||||
for (let k = 0; k < data.length; k++) {
|
||||
if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) {
|
||||
if (!temp[data[k][pid]]['children']) {
|
||||
temp[data[k][pid]]['children'] = []
|
||||
}
|
||||
if (!temp[data[k][pid]]['_level']) {
|
||||
temp[data[k][pid]]['_level'] = 1
|
||||
}
|
||||
data[k]['_level'] = temp[data[k][pid]]._level + 1
|
||||
temp[data[k][pid]]['children'].push(data[k])
|
||||
} else {
|
||||
res.push(data[k])
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
export function formatNumberByUnit (value: any) {
|
||||
if (value) {
|
||||
value = value.toString()
|
||||
const negativeFlag = value.indexOf('-') > -1
|
||||
if (value.indexOf('-') > -1) {
|
||||
value = value.replaceAll('-', '')
|
||||
}
|
||||
if (Number(value) >= 100000000) {
|
||||
return `${negativeFlag ? '-' : ''}${Math.round(Number(value) / 1000000) / 100 + '亿'}`
|
||||
} else if (Number(value) >= 10000) {
|
||||
return `${negativeFlag ? '-' : ''}${Math.round(Number(value) / 100) / 100 + '万'}`
|
||||
} else {
|
||||
return `${negativeFlag ? '-' : ''}${value}`
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function formatNumber (n: any) {
|
||||
if (n) {
|
||||
n = n.toString()
|
||||
const negativeFlag = n.indexOf('-') > -1
|
||||
if (n.indexOf('-') > -1) {
|
||||
n = n.replaceAll('-', '')
|
||||
}
|
||||
const numArr = n.toString().split('.')
|
||||
const decmial = numArr[1] ? '.' + numArr[1] : ''
|
||||
const a = numArr[0]
|
||||
const b = parseInt(a).toString()
|
||||
const len = a.length
|
||||
if (len <= 3) {
|
||||
return negativeFlag ? `-${b + decmial}` : b + decmial
|
||||
}
|
||||
const r = len % 3
|
||||
const returnNumber = r > 0
|
||||
? b.slice(0, r) + ',' + (b.slice(r, len).match(/\d{3}/g) || []).join(',') + decmial : (b.slice(r, len).match(/\d{3}/g) || []).join(',') + decmial
|
||||
return negativeFlag ? `-${returnNumber}` : returnNumber
|
||||
} else {
|
||||
return '0'
|
||||
}
|
||||
}
|
||||
|
||||
export function padZeroAfterDecimal (num: number) {
|
||||
let numStr = num.toString()
|
||||
if (numStr.includes('.')) {
|
||||
const parts = numStr.split('.')
|
||||
let decimalPart = parts[1]
|
||||
if (decimalPart.length < 2) {
|
||||
decimalPart = decimalPart.padEnd(2, '0')
|
||||
} else {
|
||||
decimalPart = decimalPart.slice(0, 2)
|
||||
}
|
||||
numStr = parts[0] + '.' + decimalPart
|
||||
} else {
|
||||
numStr = numStr + '.00'
|
||||
}
|
||||
return numStr
|
||||
}
|
||||
|
||||
export function getHtmlByValue(
|
||||
val: any,
|
||||
unit: any,
|
||||
nullStr = '',
|
||||
compareVal = 0,
|
||||
raiseFlag = '+',
|
||||
data?: any,
|
||||
color?: any
|
||||
) {
|
||||
if (val === null || val === undefined) return nullStr
|
||||
if (parseFloat(val) === compareVal) {
|
||||
return `<b style="color: ${color || '#1b9c35'}">${data || val}${unit}</b>`
|
||||
} else if (parseFloat(val) > compareVal) {
|
||||
return `<b style="color: ${color || 'red'}">${raiseFlag}${data || val}${unit}</b>`
|
||||
} else {
|
||||
return `<b style="color: ${color || '#07c160'}">${data || val}${unit}</b>`
|
||||
}
|
||||
}
|
||||
11
src/utils/mitt.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import mitt from 'mitt'
|
||||
|
||||
type Events = {
|
||||
showLoading: string
|
||||
hiddenLoading: string
|
||||
noteType: string
|
||||
setTitle: object
|
||||
}
|
||||
|
||||
const emitter = mitt<Events>()
|
||||
export default emitter
|
||||
92
src/utils/request.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
|
||||
import router from '@/router'
|
||||
import emitter from '@/utils/mitt'
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
class HttpRequest {
|
||||
getInsideConfig() {
|
||||
const config = {
|
||||
baseURL: import.meta.env.VITE_BASE_URL,
|
||||
timeout: 600000,
|
||||
withCredentials: false,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// 请求拦截
|
||||
interceptors(instance: AxiosInstance, url: string | number | undefined) {
|
||||
instance.interceptors.request.use(
|
||||
(config: any) => {
|
||||
const userInfo = userInfoStore()
|
||||
config.headers.token = userInfo.token
|
||||
return config
|
||||
},
|
||||
(error: any) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截
|
||||
instance.interceptors.response.use(
|
||||
(res: any) => {
|
||||
const { data } = res
|
||||
console.log('返回数据处理', res)
|
||||
if (data.code === 0) {
|
||||
return data
|
||||
} else if (data.code === 401 || data.code === 404) {
|
||||
const userInfo = userInfoStore()
|
||||
userInfo.token = ''
|
||||
router.push({
|
||||
name: 'login',
|
||||
query: {
|
||||
// currentRoute存储了当前路由信息的对象
|
||||
redirect: router.currentRoute.value.fullPath
|
||||
}
|
||||
})
|
||||
emitter.emit('hiddenLoading', '')
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
},
|
||||
(error: any) => {
|
||||
console.log('error==>', error)
|
||||
if (error.response && (error.response.status === 401)) {
|
||||
const userInfo = userInfoStore()
|
||||
userInfo.token = ''
|
||||
router.push({
|
||||
name: 'login',
|
||||
query: {
|
||||
// currentRoute存储了当前路由信息的对象
|
||||
redirect: router.currentRoute.value.fullPath
|
||||
}
|
||||
})
|
||||
}
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
request(options: AxiosRequestConfig, openDefaultData = false) {
|
||||
const instance = axios.create()
|
||||
options = Object.assign(this.getInsideConfig(), options)
|
||||
if (!options.url) {
|
||||
throw new Error('URL is not defined in request options')
|
||||
}
|
||||
|
||||
this.interceptors(instance, options.url)
|
||||
if (options.data && openDefaultData) {
|
||||
options.data = JSON.stringify(options.data)
|
||||
}
|
||||
if (options.url.includes('?')) {
|
||||
options.url = options.url + '&t=' + new Date().getTime()
|
||||
} else {
|
||||
options.url = options.url + '?t=' + new Date().getTime()
|
||||
}
|
||||
return instance(options)
|
||||
}
|
||||
}
|
||||
|
||||
const http = new HttpRequest()
|
||||
export default http
|
||||
326
src/utils/types.ts
Normal file
@@ -0,0 +1,326 @@
|
||||
export interface CommunicateObj {
|
||||
showPop1: boolean
|
||||
showPop2: boolean
|
||||
showPop3: boolean
|
||||
showPop4: boolean
|
||||
showPop5: boolean
|
||||
showPop6: boolean
|
||||
popTitle: string
|
||||
type: string
|
||||
linkId: string
|
||||
value1: string[]
|
||||
value3: string[]
|
||||
value4: string[]
|
||||
value5: string[]
|
||||
linkTypeName: string
|
||||
selectlinkType: string
|
||||
linkTypeList: any[]
|
||||
dicNote2: string
|
||||
dicNote3: string
|
||||
sysDay: string
|
||||
selectsysDay: any[]
|
||||
sysTime: string
|
||||
selectsysTime: any[]
|
||||
cusName: string
|
||||
searchValue: string
|
||||
selectcus: string
|
||||
showPop2List: any[]
|
||||
cusLevelName: string
|
||||
selectcusLevel: string
|
||||
linkTitle: string
|
||||
cusLevelList: any[]
|
||||
cusUserNames: string
|
||||
cusUserList: any[]
|
||||
selectcusUserIds: any[]
|
||||
staffNames: string
|
||||
staffList: any[]
|
||||
selectstaffIds: any[]
|
||||
saleNames: string
|
||||
saleList: any[]
|
||||
selectsaleIds: any[]
|
||||
linkFromName: string
|
||||
linkFrom: number
|
||||
linkFromRadio: string
|
||||
linkFromField: string
|
||||
linkFromList: any[]
|
||||
linkWayName: string
|
||||
selectlinkWay: string
|
||||
linkWayList: any[]
|
||||
linkNote: string
|
||||
meetingName: string
|
||||
selectmeeting: string
|
||||
staffNote: string
|
||||
cusLinkStockList: any[]
|
||||
cusLinkStockListForLinkId: any[]
|
||||
saleScore: number
|
||||
saleNote: string
|
||||
[key: string]: any
|
||||
handleShowPop1: (val: string, title: string) => void
|
||||
handleShowPop2: (val: string, title: string) => void
|
||||
handleShowPop3: (val: string, title: string) => void
|
||||
handleShowPop4: (val: string, title: string, index: number) => void
|
||||
handleShowPop5: (val: string, title: string) => void
|
||||
handleShowPop6: (index: number) => void
|
||||
onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
onSearchInput: () => any
|
||||
deleteStock: (index: number) => void
|
||||
onConfirm3: () => void
|
||||
onConfirm4: ({ selectedValues }: { selectedValues?: any[] }) => void
|
||||
changeLinkFrom: () => void
|
||||
onConfirm5: () => void
|
||||
}
|
||||
|
||||
export interface CusObj {
|
||||
showPop1: boolean
|
||||
showPop2: boolean
|
||||
showPop3: boolean
|
||||
showPop4: boolean
|
||||
popTitle: string
|
||||
type: string
|
||||
value1: string[]
|
||||
value3: string[]
|
||||
value4: string[]
|
||||
cusName: string
|
||||
cusLevelName: string
|
||||
selectcusLevel: string
|
||||
cusLevelList: any[]
|
||||
cusUserName: string
|
||||
selectcusUser: string
|
||||
searchValue: string
|
||||
showPop2List: any[]
|
||||
companyTypeName: string
|
||||
selectcompanyType: string
|
||||
companyTypeList: any[]
|
||||
cityName: string
|
||||
selectcity: string
|
||||
cityList: any[]
|
||||
address: string
|
||||
accountStatus: string
|
||||
accountTime: string
|
||||
selectaccountTime: any[]
|
||||
accountTypeNames: string
|
||||
selectaccountTypeIds: any[]
|
||||
accountTypeList: any[]
|
||||
isMoney: string
|
||||
tradeMoney: string
|
||||
capitalScaleName: string
|
||||
selectcapitalScale: string
|
||||
capitalScaleList: any[]
|
||||
capitalScaleValue: string
|
||||
fundNum: string
|
||||
investOverseasPer: string
|
||||
investResearchNum: string
|
||||
investChannelName: string
|
||||
selectinvestChannel: string
|
||||
investChannelList: any[]
|
||||
website: string
|
||||
companyCreateTime: string
|
||||
selectcompanyCreateTime: any[]
|
||||
introduction: string
|
||||
saleUserNames: string
|
||||
selectsaleUserIds: any[]
|
||||
saleUserList: any[]
|
||||
[key: string]: any
|
||||
handleShowPop1: (val: string, title: string) => void
|
||||
handleShowPop2: (val: string, title: string) => void
|
||||
handleShowPop3: (val: string, title: string) => void
|
||||
handleShowPop4: (val: string, title: string) => void
|
||||
onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
onSearchInput: () => any
|
||||
onConfirm3: () => void
|
||||
onConfirm4: ({ selectedValues }: { selectedValues?: any[] }) => void
|
||||
setAccountStatus: () => any
|
||||
}
|
||||
|
||||
export interface CusUserObj {
|
||||
showPop1: boolean
|
||||
showPop3: boolean
|
||||
popTitle: string
|
||||
type: string
|
||||
value1: string[]
|
||||
value3: string[]
|
||||
cusUserName: string
|
||||
positionName: string
|
||||
selectposition: string
|
||||
positionList: any[]
|
||||
saleUserNames: string
|
||||
selectsaleUserIds: any[]
|
||||
saleUserList: any[]
|
||||
selectcusUser: string
|
||||
phone: string
|
||||
mobile: string
|
||||
email: string
|
||||
wxName: string
|
||||
address: string
|
||||
preference: string
|
||||
[key: string]: any
|
||||
handleShowPop1: (val: string, title: string) => void
|
||||
handleShowPop3: (val: string, title: string) => void
|
||||
onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
onConfirm3: () => void
|
||||
}
|
||||
|
||||
export interface MeetingObj {
|
||||
showPop1: boolean
|
||||
showPop2: boolean
|
||||
showPop3: boolean
|
||||
showPop4: boolean
|
||||
showPop5: boolean
|
||||
showPop6: boolean
|
||||
popTitle: string
|
||||
type: string
|
||||
value1: string[]
|
||||
value3: string[]
|
||||
meetingTitle: string
|
||||
sysDay: any
|
||||
selectsysDay: any[]
|
||||
sysTime: string
|
||||
selectsysTime: any[]
|
||||
meetingTypeName: string
|
||||
selectmeetingType: string
|
||||
meetingTypeList: any[]
|
||||
meetingByName: string
|
||||
selectmeetingBy: string
|
||||
meetingByList: any[]
|
||||
stockCodeList: any[]
|
||||
searchValue: string
|
||||
showPop2List: any[]
|
||||
meetingGuestList: any[]
|
||||
linkFromName: string
|
||||
linkFrom: number
|
||||
linkFromRadio: string
|
||||
linkFromField: string
|
||||
linkFromList: any[]
|
||||
phoneBy: string
|
||||
netBy: string
|
||||
meetingPassword: string
|
||||
organizerNames: string
|
||||
selectorganizerIds: any[]
|
||||
organizerList: any[]
|
||||
content: string
|
||||
visibleName: string
|
||||
selectvisible: string
|
||||
visibleList: any[]
|
||||
staffNames: string
|
||||
staffList: any[]
|
||||
selectstaffIds: any[]
|
||||
[key: string]: any
|
||||
handleShowPop1: (val: string, title: string) => void
|
||||
handleShowPop3: (val: string, title: string) => void
|
||||
handleShowPop4: (val: string, title: string, index: number) => void
|
||||
handleShowPop5: (val: string, title: string) => void
|
||||
handleShowPop6: (val: string, title: string, index: number) => void
|
||||
onSearchInput: () => any
|
||||
deleteItemByType: (val: string, index: number) => void
|
||||
onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
onConfirm3: () => void
|
||||
onConfirm4: ({ selectedValues }: { selectedValues?: any[] }) => void
|
||||
changeLinkFrom: () => void
|
||||
onConfirm5: () => void
|
||||
}
|
||||
|
||||
export interface GroupObj {
|
||||
stockGroupName: string
|
||||
isPublic: string
|
||||
}
|
||||
|
||||
export interface HisObj {
|
||||
sysDate: any
|
||||
selectsysDate: any[]
|
||||
groupCodeId: any
|
||||
id: any
|
||||
dividend24Percent: string
|
||||
dividend25Percent: string
|
||||
showPop: boolean
|
||||
value: string[]
|
||||
type: string
|
||||
popTitle: string
|
||||
handleShowPop: (val: string, title: string) => void
|
||||
onConfirm: ({ selectedValues }: { selectedValues?: any[] }) => void
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface BuyOrSellObj {
|
||||
groupId: any
|
||||
tradeTime: any
|
||||
selecttradeTime: any[]
|
||||
isSell: number
|
||||
num: number
|
||||
stockName: string
|
||||
stockCode: string
|
||||
tradePrice: string
|
||||
note: string
|
||||
showPop: boolean
|
||||
showPop2: boolean
|
||||
searchValue: string
|
||||
value: string[]
|
||||
type: string
|
||||
popTitle: string
|
||||
handleShowPop: (val: string, title: string) => void
|
||||
handleShowPop2: (val: string, title: string) => void
|
||||
onConfirm: ({ selectedValues }: { selectedValues?: any[] }) => void
|
||||
onSearchInput: () => any
|
||||
onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface bonnusObj {
|
||||
groupId: any
|
||||
stockName: string
|
||||
selectstock: string
|
||||
stockList: any[]
|
||||
dividendDate: any
|
||||
selectdividendDate: any[]
|
||||
exDividendDate: any
|
||||
selectexDividendDate: any[]
|
||||
dps: number
|
||||
sg1: any
|
||||
sg2: any
|
||||
note: string
|
||||
showPop: boolean
|
||||
showPop2: boolean
|
||||
value: string[]
|
||||
value1: string[]
|
||||
type: string
|
||||
popTitle: string
|
||||
handleShowPop: (val: string, title: string) => void
|
||||
handleShowPop2: (val: string, title: string) => void
|
||||
onConfirm: ({ selectedValues }: { selectedValues?: any[] }) => void
|
||||
onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void
|
||||
[key: string]: any
|
||||
}
|
||||
export interface IHoldList {
|
||||
grouId?: number
|
||||
stockCode?: string | number
|
||||
stockName?: string
|
||||
stockPricePercent?: number | string
|
||||
stockPrice?: number | string
|
||||
costPrice?: number | string
|
||||
hkStockMarket?: number | string
|
||||
holdPercent?: string | number
|
||||
todayIncome?: number | string
|
||||
totalIncome?: number | string
|
||||
holdCost?: number | string
|
||||
shares?: number | string
|
||||
dividendMoney?: number | string
|
||||
priceIncome?: number | string
|
||||
dividendTaxMoney?: number | string
|
||||
}
|
||||
export interface IDicList {
|
||||
id: number
|
||||
dicType: string
|
||||
dicKey: string
|
||||
dicValue: string
|
||||
dicNote: string
|
||||
dicNote2: string
|
||||
dicNote3: string
|
||||
dicColor: string
|
||||
dicSort: string
|
||||
}
|
||||
export interface IUserLog {
|
||||
note: string
|
||||
userName: string
|
||||
}
|
||||
90
src/views/comment/detail.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="normal-title f-b text-center inboxTitle p-10">{{ workInbox.inboxTitle }}</div>
|
||||
<div class="text-center">
|
||||
<van-tag type="primary" size="medium" v-if="workInbox.stockCode">
|
||||
{{ workInbox.stockCode }}【{{ workInbox.stockName }}】
|
||||
</van-tag>
|
||||
<van-tag type="success" size="medium" class="ms-10">{{ workInbox.inboxTypeSecondName }}</van-tag>
|
||||
</div>
|
||||
<div class="content text-end mb-5 pe-16">{{ workInbox.firstChargeUserName }}</div>
|
||||
<div class="content text-end pe-16">{{ workInbox.createTime }}</div>
|
||||
<div v-html="workInbox.inboxContent" class="p-16 inboxContent content"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import emitter from '@/utils/mitt'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const init = () => {
|
||||
if (route.query && route.query.inboxId) {
|
||||
emitter.emit('showLoading', '')
|
||||
let inboxId = route.query.inboxId
|
||||
getWorkInboxDetail(inboxId)
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
|
||||
let workInbox = ref({
|
||||
inboxTitle: '',
|
||||
stockCode: '',
|
||||
stockName: '',
|
||||
inboxTypeSecondName: '',
|
||||
firstChargeUserName: '',
|
||||
createTime: '',
|
||||
inboxContent: ''
|
||||
})
|
||||
|
||||
import { workInboxInfo } from '@/utils/api'
|
||||
const getWorkInboxDetail = async (inboxId: any) => {
|
||||
const { data } = await workInboxInfo(inboxId)
|
||||
workInbox.value = data.workInbox
|
||||
emitter.emit('setTitle', { title: workInbox.value.inboxTitle, type: 'comment' })
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
<style lang='scss' scoped>
|
||||
.inboxContent {
|
||||
margin: 12px 16px 0;
|
||||
width: calc(100vw - 32px);
|
||||
line-height: 20px;
|
||||
font-size: 16px;
|
||||
overflow-y: auto;
|
||||
height: calc(100vh - 140px);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
margin: 12px 0;
|
||||
width: calc(674px);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(table) {
|
||||
border-top: 1px solid #ccc;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
|
||||
:deep(td) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(img) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(pre) {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.inboxTitle {
|
||||
max-width: 678px;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
125
src/views/comment/list.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="page-content" ref="pageContent">
|
||||
<div class="head">
|
||||
<van-image class="bg" src="/img/commentBg.jpg" fit="cover" />
|
||||
<div class="headDiv p-16">
|
||||
<div class="headTop">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<div class="border-bottom-1">
|
||||
<span class="day">{{ day }}</span>
|
||||
<span class="month1 ms-10">{{ month1 }}</span>
|
||||
</div>
|
||||
<div class="textSize text-end">{{ year }}</div>
|
||||
</div>
|
||||
<van-image class="logo border-radius-4" src="/img/completeLogo.jpg" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="border-radius-4 border-1 text-center p-8">
|
||||
<div>{{ month2 }}</div>
|
||||
<div>{{ week }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="textSize">
|
||||
<div>第一上海</div>
|
||||
<div class="mt-2">精选点评,让你了解更多行业新闻</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" v-if="list.length > 0">
|
||||
<van-list class="mt-10" v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"
|
||||
:immediate-check="false">
|
||||
<van-cell v-for="item in list" :key="item.inboxId"
|
||||
@click="$router.push({ name: 'comment-detail', query: { inboxId: item.inboxId } })">
|
||||
<div class="normal-title text-start balck-text-color f-b ellipsis-one">{{ item.inboxTitle }}</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="content">{{ item.stockName ? item.stockName : '' }}</div>
|
||||
<div class="content">{{ item.createTime }}</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { getWeek } from '@/utils/index'
|
||||
import moment from 'moment'
|
||||
import { listLoadAndRefresh } from '@/mixins/list-load-and-refresh'
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
|
||||
let year = ref(moment().format('YYYY'))
|
||||
let month1 = ref(moment().format('MMM'))
|
||||
let month2 = ref(moment().format('M') + '月')
|
||||
let week = ref(getWeek(moment().format('d')))
|
||||
let day = ref(moment().format('DD'))
|
||||
|
||||
let pageContent = ref(null)
|
||||
const scrollPosition = ref(0)
|
||||
|
||||
const refresh = () => {
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
import { workInboxList } from '@/utils/api'
|
||||
|
||||
const getData = async () => {
|
||||
const { data } = await workInboxList({
|
||||
curPage: curPage.value,
|
||||
limit: 20,
|
||||
inboxType: 4
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
const { refreshing, finished, loading, curPage, list, onLoad, onRefresh } = listLoadAndRefresh(getData, 'comment')
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, refresh)
|
||||
|
||||
setScrollTop()
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'comment-detail')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.page-content {
|
||||
|
||||
.head {
|
||||
position: relative;
|
||||
|
||||
.bg {
|
||||
width: 100%;
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
.headDiv {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
background: #171b2eb3;
|
||||
|
||||
.headTop {
|
||||
.day {
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
.month1 {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 57px;
|
||||
width: 132px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
11
src/views/common/error.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<van-empty description="404" image="network">
|
||||
<van-button size="mini" type="primary" plain @click="$router.go(-1)" class="bottom-button">返回上一页</van-button>
|
||||
<van-button size="mini" type="success" @click="$router.push({ name: 'home' })"
|
||||
class="bottom-button">进入首页</van-button>
|
||||
</van-empty>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
<style lang="scss"></style>
|
||||
195
src/views/common/loading-page.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="loading-page">
|
||||
<div>
|
||||
<div class="grid">
|
||||
<div class="item-animation item-animation1">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="item-animation item-animation2">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="item-animation item-animation3">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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" />
|
||||
</div>
|
||||
<div class="item-animation item-animation4">
|
||||
<img
|
||||
src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCABPAE4DASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8if2/yP8Ahuv9tsf9Xb/tHj65+MHjiP8A9FyOv456gEeU/Bd/C8Ws39z4jv7azuoLWP8AsiG4mNut5dy+fxG/2W9iUJsgyJVZf3uCOMV6n/wUA/5Pu/bcBHP/AA1x+0hznoP+FyeNscfSvkVRuiKdN5+VxkFPu5KkYKngHKkEYBBr+36WQVOKPDXC5FHEVML9ay+hy1qU3TnGTVm1KLTTemq7nieH/HFLw78Q8p4sr5ZTzdZZNTlSq2cOZNO9pK19NXo/VM/SNda025xs1jT3XBUKk9oFmGBxIsSxK/1cMe2fWKTVdLh/12qWITaF2GaExYPbG/g+/BNfnPHLNGAFnmwOmZZDj6bmP+Oeae1xO33ppW9mdmH5EkfpX8v1/oXYXE1/rFfiByqO/vzoylNXafxSpdP8Vn8z/ULC/tW8wwlCjQocFYOlTowhCEaeIUIxUVFRUYxqpRSvolaz/D9Ev+Eg0Ij5dZ0UDOMfa4lH/jxP1/rT/wC1NEJ51fRzx21S3P8AJsfyr88C+eCM/U5/mDUeIx0jUf5+lYf8SRZO7c3EUlbZey0+y9O3X5fI61+1pztbcI4VNtS0xX2vd1v7bfz6389P0Im8SeGoRmbxBo9u38QutRhhDf8AXIgSg/j196rP418HRLvbxd4aZT90Lrmku7fWJbz7QPwt6+AFWJQQIl59cnH+6Tyv4EVWLNncCQDjhPlP1BBBH4f/AF66v+JH+H5RpL+2qjejbS1e2/Xo15nmVv2sfFkqnNT4awTimtI4iX93rff166XV0forYa/oeqy+VpeqWGoyMcCKyuVupB9BExz+p/rrOMMRgD2Bz+tfnHpupX2k3K3lhcm3uY+YphHFIkR/6ehMjqOvpX6F+HtQXWvDej63GskP9o2sM7xsQ7RSPGGZDwe/PtjAwMV/Lfjx9H2fhZ9VzDD1p18sxVath1Gm3Kv7SHsuSSjL3eWSbellq9nv/oN9Dn6ZdH6ReOzHKcXl8stzTA4RYitTnKnOm0rO9Ne1dvkk9O1zx7/goF/yfr+26vb/AIa4/aQwP+6yeMu/XoW/P6Y+RwMcCvrf/goH/wAn8ftt/wDZ2/7SX/q5PGFfJFf618Be9wvkl9bZdh7X17H/AC75zpiqltLp3t1slv3Ciiivuo04uKvr2vZ2+/8A4B4yhFpPr36r/htnfUdvb1/Qf4Ub29f0H+FNop+zi9/0/wAheyh2X3L/AC8l9w7e3r+g/wAKYcnocH6D+VLRVRpQ5lovLRaemnkNU4rbT0sv0GuSqcHjHzjAxJ/vjo3/AALP1r9Afh0zL4G8OqpAA061YfKccm4Q+g+7Gg9eMnkmvz8lAKNnsM/jX6E/Dcf8UZ4dU8j+ybfAJPG24vFHOc8Af5PNfw99NuCfBOBdklDHUFG32b+wuo9k76pWuf64fsnYr/iI2fNbvLq0b315YqdlddNtPTpY8x/4KB/8n8ftt/8AZ2/7SX/q5PGFfJFfW/8AwUCB/wCG+P23Dnj/AIa4/aQ49AfjJ41/PO1fp+dfJFf1FwH/AMktkn/Yuw/5H+VGdf71P0l+SCiiivuYt2Wr2XXyPIh8K+f5sKKKKd33f3soKKKKqLfMtX977AMk+430r9Avhs7HwZ4cOTzpEHUAH/j6vfavz9k+430r9APht/yJnhz/ALBFv/6U3tfxL9Nn/kisIun1+H4PD2+7p2P9bP2UH/Jw8+/7F9f/ANvPOv8AgoFn/hvH9tt+/wDw1z+0eM/T4y+OuMdP4V7dvrXyMOQPoK+uf+CgJ/4zv/bbHb/hrr9pA/8AmZfHgr5EQkopPXFf0/wL/wAkvkv/AGL6H5H+VOcfx6j68qe3oPooor7eOy9F+R48PhXz/NhRRRTKCiiihtpNrRpO33Ey+F/11EIB4NffnwzJPgnw2TyTo9vn/wACr2vgSvvr4Zf8iT4a/wCwPb/+lV7j86/h36a1ST4NwCeq+tYd/OXsbve2v+Vz/W/9lLp4hZ+1o/7On07xlf7+p55/wUB/5Pw/bb/7O5/aQ/8AVy+PK+RY/uL9K+uv+CgP/J+H7bf/AGdz+0h/6uXx5XyLH9xfpX9VcC/8kvkv/Yvofkf5UZv/ABqn+FfoPooor7eOy9F+R48PhXz/ADYUUUUygooopS2fo/yJn8L+X5oUckfUV99fDAhvBfhgMMj+xIcgMRyJ5SDxju78e/sK+BV6j6j+dffPwvP/ABRvhoYxjRov/RzV/Dn01f8Aki8E+qxGF/8AcR/rd+ym/wCTgZ5/2LZf+kVD/9k=">
|
||||
</div>
|
||||
<div class="item-animation item-animation5">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="item-animation item-animation6">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="item-animation item-animation7">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="item-animation item-animation8">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
<div class="item-animation item-animation9">
|
||||
<img
|
||||
src="data:image/jpeg;base64,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">
|
||||
</div>
|
||||
</div>
|
||||
<div class="normal-title f-b mb-5 mt-5">第一上海證券有限公司/第一上海期貸有限公司</div>
|
||||
<div class="sub-title mb-5">FIRST SHANGHAI SECURITIES LIMITED</div>
|
||||
<div class="tip mb-5">香港中環德輔道中 71 號永安集團大廈 19 樓D</div>
|
||||
<div class="tip mb-5">19/F, Wing On House, 71 Des Voeux Road Central, Hong Kong</div>
|
||||
<div class="tip mb-5">電話 Tel:(852)2532 1580 傳真 Fax:(852)2537 6911</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
const userInfo = userInfoStore()
|
||||
const getCode = () => {
|
||||
const code = getUrlParam('code') // 截取路径中的code,如果没有就去微信授权,如果已经获取到了就直接传code给后台获取openId
|
||||
const local = window.location.href
|
||||
if (code == null || code === '') {
|
||||
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx1d52f91f259a691f&redirect_uri=' + encodeURIComponent(local) + '&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect'
|
||||
} else {
|
||||
userInfo.code = code
|
||||
window.location.href = 'http://h5.szzztec.com/login'
|
||||
}
|
||||
}
|
||||
|
||||
const getUrlParam = (name: string) => {
|
||||
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
|
||||
var r = window.location.search.substr(1).match(reg)
|
||||
if (r != null) return unescape(r[2])
|
||||
return null
|
||||
}
|
||||
|
||||
getCode()
|
||||
</script>
|
||||
<style lang='scss' scoped>
|
||||
.loading-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
max-width: 678px;
|
||||
height: 100vh;
|
||||
margin: 0 auto;
|
||||
|
||||
.grid {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
margin: 0 auto;
|
||||
|
||||
.item-animation {
|
||||
width: 33%;
|
||||
height: 33%;
|
||||
float: left;
|
||||
-webkit-animation: itemFlicker 1.3s infinite ease-in-out;
|
||||
animation: itemFlicker 1.3s infinite ease-in-out;
|
||||
background-size: cover;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-animation1 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.item-animation2 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.item-animation3 {
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.item-animation4 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.item-animation5 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.item-animation6 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.item-animation7 {
|
||||
-webkit-animation-delay: 0;
|
||||
animation-delay: 0;
|
||||
}
|
||||
|
||||
.item-animation8 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.item-animation9 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
.text1 {
|
||||
font-size: 18px;
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text2 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.text3 {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes itemFlicker {
|
||||
|
||||
0%,
|
||||
70%,
|
||||
100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes itemFlicker {
|
||||
|
||||
0%,
|
||||
70%,
|
||||
100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
112
src/views/common/log.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="title">請求詳情</div>
|
||||
<div class="request">
|
||||
<div class="request-detail">
|
||||
<div class="name">note</div>
|
||||
<div class="content">{{ logData.note }}</div>
|
||||
</div>
|
||||
<div class="request-detail">
|
||||
<div class="name">userName</div>
|
||||
<div class="content">{{ logData.userName }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title mt-16">日志详情</div>
|
||||
<div class="card mt-16" v-for="item in logList" :key="item.id">
|
||||
<div class="d-flex pb-16 border-bottom-1 justify-content-between">
|
||||
<div>类型:<span :style="{ color: item.logLevelColor }">{{ item.logLevelName }}</span></div>
|
||||
<div class="create-time">创建时间:{{ item.createTime }}</div>
|
||||
</div>
|
||||
<div class="d-flex mt-16">
|
||||
详情:
|
||||
<div class="note flex-1" :class="!item.showAll ? 'text-ellipsis' : ''">{{ item.note }}
|
||||
<div style="margin-top: 4px;" v-if="item.logLevel === 40000">错误信息:{{ item.errorMsg }}</div>
|
||||
</div>
|
||||
<span class="activeColor" @click="item.showAll = !item.showAll">{{ item.showAll ? '收起' : '展开' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { type IUserLog } from '@/utils/types'
|
||||
const logData = reactive<IUserLog>({
|
||||
note: '',
|
||||
userName: ''
|
||||
})
|
||||
const logList = ref<any[]>([])
|
||||
import emitter from '@/utils/mitt'
|
||||
import { sysLog } from '@/utils/api'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const requestId = ref()
|
||||
const init = () => {
|
||||
if (route.query && route.query.requestId) {
|
||||
requestId.value = route.query.requestId
|
||||
getData()
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
const getData = async () => {
|
||||
emitter.emit('showLoading', '')
|
||||
const { data } = await sysLog(requestId.value)
|
||||
logData.note = data.sysUserLog.note
|
||||
logData.userName = data.sysUserLog.userName
|
||||
logList.value = data.sysUserLogDetail.map((ele: any) => {
|
||||
ele.showAll = false
|
||||
return ele
|
||||
})
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-content {
|
||||
padding: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.pb-10{
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
table {
|
||||
width: 100vw;
|
||||
overflow: auto;
|
||||
}
|
||||
.create-time {
|
||||
font-size: 12px;
|
||||
color: #909090;
|
||||
}
|
||||
.title {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 4px 0;
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
.request {
|
||||
border: 1px solid #e2e2e2;
|
||||
.request-detail {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e2e2e2;
|
||||
.name {
|
||||
width: 100px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #e2e2e2;
|
||||
}
|
||||
.content {
|
||||
word-break: break-all;
|
||||
padding: 10px;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
color: #1989fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
.note {
|
||||
word-break: break-all;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
182
src/views/common/login.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="page-div">
|
||||
<div class="form-container d-flex align-center">
|
||||
<div class="form">
|
||||
<div class="text-center pt-20">
|
||||
<van-image height="100" width="100" src="/img/smallLogo.jpg" round />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h2 class="big-title f-b mt-16">第一上海</h2>
|
||||
<div class="sub-title mt-16">欢迎登录数据管理系统</div>
|
||||
</div>
|
||||
<van-form class="control-area" @submit="onSubmit" validate-trigger="onSubmit">
|
||||
<van-field v-model="username" name="username" placeholder="请输入您的账户" label="账户"
|
||||
:rules="[{ required: true, message: '请输入您的账户' }]" autocomplete="new-password" />
|
||||
<van-field v-model="password" name="password" placeholder="请输入您的密码" label="密码"
|
||||
:rules="[{ required: true, message: '请输入您的密码' }]" type="password" autocomplete="new-password" />
|
||||
<div class="opr-area">
|
||||
<van-button round block type="primary" :disabled="submitFlag" native-type="submit">
|
||||
确定
|
||||
</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="company-info">
|
||||
<div class="text-center mt-16 mb-16">
|
||||
<div class="mb-16 normal-title">第一上海证券有限公司</div>
|
||||
<div class="mb-8 sub-title">FIRST SHANGHAI SECURITIES LIMITED</div>
|
||||
<div class="mb-5 tip">香港中環德輔道中 71 號永安集團大廈 19 樓D</div>
|
||||
<div class="mb-5 tip">19/F, Wing On House, 71 Des Voeux Road Central, Hong Kong</div>
|
||||
<div class="mb-5 tip">电话 Tel:(852)2532 1580 传真 Fax:(852)2537 6911</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
const userInfo = userInfoStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
let username = ref(userInfo.username || '')
|
||||
let password = ref(userInfo.password || '')
|
||||
let submitFlag = ref(false)
|
||||
import { encrypt } from '@/utils/index'
|
||||
import { sysLogin, sysUserInfo, sysMenuNav } from '@/utils/api'
|
||||
const onSubmit = async () => {
|
||||
submitFlag.value = true
|
||||
const data: any = await sysLogin(
|
||||
{
|
||||
username: username.value,
|
||||
password: encrypt(password.value),
|
||||
}
|
||||
)
|
||||
if (data && data.code === 0) {
|
||||
userInfo.username = username.value
|
||||
userInfo.password = password.value
|
||||
userInfo.token = data.token
|
||||
getUserInfo()
|
||||
} else {
|
||||
submitFlag.value = false
|
||||
showToast('账号密码错误')
|
||||
}
|
||||
}
|
||||
|
||||
const getUserInfo = async () => {
|
||||
const data: any = await sysUserInfo()
|
||||
userInfo.id = data.user.userId
|
||||
userInfo.realname = data.user.realname
|
||||
userInfo.headPic = data.user.headPic
|
||||
userInfo.roleId = data.user.roleId + ''
|
||||
getsysMenuNav()
|
||||
}
|
||||
|
||||
const getsysMenuNav = async () => {
|
||||
const data: any = await sysMenuNav()
|
||||
userInfo.permissions = data.permissions
|
||||
let redirect = route.query.redirect || '/'
|
||||
if (typeof redirect !== 'string') {
|
||||
redirect = '/'
|
||||
}
|
||||
router.replace(redirect)
|
||||
}
|
||||
</script>
|
||||
<style lang='scss' scoped>
|
||||
.form-container {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
|
||||
.form {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
padding: 16px;
|
||||
margin: 16px;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 40px;
|
||||
margin: 40px;
|
||||
}
|
||||
|
||||
.pt-20 {
|
||||
padding-top: 20px;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.control-area {
|
||||
margin: 32px auto;
|
||||
padding: 0 16px;
|
||||
width: 100%;
|
||||
@media screen and (min-width: 768px) {
|
||||
width: 100%;
|
||||
margin: 40px auto;
|
||||
}
|
||||
|
||||
.van-cell {
|
||||
padding: 16px;
|
||||
font-size: 16px;
|
||||
margin: 0 0 16px;
|
||||
width: 100%;
|
||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .1);
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 20px;
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.van-field__label) {
|
||||
padding-right: 16px;
|
||||
|
||||
label {
|
||||
max-width: 5em;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opr-area {
|
||||
padding: 16px 0;
|
||||
|
||||
.van-button {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-button--disabled {
|
||||
color: rgba(0, 0, 0, 0.15);
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.company-info {
|
||||
margin-top: 30px;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
padding: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
53
src/views/common/subscription.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<van-list class="pt-10">
|
||||
<van-cell v-for="item in subscriptionList" :key="item.id" :title="item.subscriptionItemName">
|
||||
<template #value>
|
||||
<van-switch size="16" :disabled="item.disabled" v-model="item.isSubscription" @change="changeSub(item)" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getSubscriptionList, delSubscription, saveSubscription } from '@/utils/api'
|
||||
import { ref } from 'vue'
|
||||
const subscriptionList = ref()
|
||||
const getSubList = async () => {
|
||||
const { data } = await getSubscriptionList()
|
||||
subscriptionList.value = data.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
disabled: false,
|
||||
isSubscription: !!item.userId
|
||||
}
|
||||
})
|
||||
}
|
||||
getSubList()
|
||||
const changeSub = async (item: any) => {
|
||||
item.disabled = true
|
||||
if (!item.isSubscription) {
|
||||
await delSubscription(item.id)
|
||||
} else {
|
||||
await saveSubscription(item.subscriptionItem)
|
||||
}
|
||||
await getSubList()
|
||||
item.disabled = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pt-10 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.van-cell {
|
||||
margin: 12px 16px 0;
|
||||
width: calc(100vw - 32px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
width: 646px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
449
src/views/communicate/add-cus.vue
Normal file
@@ -0,0 +1,449 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="add-cusUser-detail detailClass">
|
||||
<van-form @submit="onSubmit" validate-trigger="onSubmit">
|
||||
<van-field name="cusName" v-model="popObj.cusName" label="机构名" placeholder="请输入机构名" :required="true"
|
||||
:rules="[{ required: true, message: '请填写机构名' }]" />
|
||||
<van-field clickable name="cusLevelName" v-model="popObj.cusLevelName" label="机构评级" placeholder="点击选择机构评级"
|
||||
@click="popObj.handleShowPop1('cusLevel', '机构评级')" readonly />
|
||||
<van-popup v-model:show="popObj.showPop1" position="bottom">
|
||||
<van-picker show-toolbar :title="popObj.popTitle" :columns="popObj[`${popObj.type}List`]"
|
||||
@confirm="popObj.onConfirm1($event)" @cancel="popObj.showPop1 = false" v-model="popObj.value1" />
|
||||
</van-popup>
|
||||
<van-field clickable name="cusUserName" v-model="popObj.cusUserName" label="核心联系人" placeholder="点击选择机构"
|
||||
@click="popObj.handleShowPop2('cusUser', '核心联系人')" readonly />
|
||||
<van-popup v-model:show="popObj.showPop2" position="bottom">
|
||||
<div class="d-flex align-items-center">
|
||||
<van-field v-model="popObj.searchValue" :placeholder="'请输入' + popObj.popTitle"
|
||||
@input="popObj.onSearchInput" />
|
||||
<van-button block type="success" size="small" native-type="button" @click="toAddCusUser"
|
||||
style="width: 60px; margin: 12px 16px 0 0">
|
||||
新增
|
||||
</van-button>
|
||||
</div>
|
||||
<van-picker show-toolbar :title="popObj.popTitle" :columns="popObj.showPop2List"
|
||||
@confirm="popObj.onConfirm2($event)" @cancel="popObj.showPop2 = false" />
|
||||
</van-popup>
|
||||
<van-field clickable name="companyTypeName" v-model="popObj.companyTypeName" label="机构类型" placeholder="点击选择机构类型"
|
||||
@click="popObj.handleShowPop1('companyType', '机构类型')" readonly />
|
||||
<van-field clickable name="cityName" v-model="popObj.cityName" label="城市" placeholder="点击选择城市"
|
||||
@click="popObj.handleShowPop1('city', '城市')" readonly />
|
||||
<van-field name="address" v-model="popObj.address" label="地址" placeholder="请输入地址" />
|
||||
<van-field name="radio" label="是否开户">
|
||||
<template #input>
|
||||
<van-radio-group v-model="popObj.accountStatus" direction="horizontal">
|
||||
<van-radio name="0">否</van-radio>
|
||||
<van-radio name="1">是</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field v-show="popObj.accountStatus === '1'" clickable name="accountTime" v-model="popObj.accountTime"
|
||||
label="开户时间" placeholder="点击选择日期" @click="popObj.handleShowPop4('accountTime', '开户时间')" readonly />
|
||||
<van-popup v-model:show="popObj.showPop4" position="bottom">
|
||||
<van-date-picker v-model="popObj.value4" :title="popObj.popTitle" @confirm="popObj.onConfirm4($event)"
|
||||
@cancel="popObj.showPop4 = false" />
|
||||
</van-popup>
|
||||
<van-field v-show="popObj.accountStatus === '1'" clickable name="accountTypeNames"
|
||||
v-model="popObj.accountTypeNames" label="开户类型" placeholder="点击选择开户类型"
|
||||
@click="popObj.handleShowPop3('accountType', '开户类型')" readonly />
|
||||
<van-popup v-model:show="popObj.showPop3" position="bottom">
|
||||
<div class="p-16 box-popup">
|
||||
<van-row class="mb-16">
|
||||
<van-col span="8">
|
||||
<van-button class="van-picker__cancel" native-type="button"
|
||||
@click="popObj.showPop3 = false">取消</van-button>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-center">
|
||||
<span class="sub-title f-b">请选择{{ popObj.popTitle }}</span>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-end">
|
||||
<van-button class="van-picker__confirm" native-type="button" @click="popObj.onConfirm3">确认</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<van-checkbox-group v-model="popObj.value3" direction="horizontal">
|
||||
<van-checkbox v-for="(item, index) in popObj[`${popObj.type}List`]" :key="index" :name="item.value"
|
||||
shape="square">
|
||||
{{ item.text }}
|
||||
</van-checkbox>
|
||||
</van-checkbox-group>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-field v-show="popObj.accountStatus === '1'" name="radio" label="是否交易">
|
||||
<template #input>
|
||||
<van-radio-group v-model="popObj.isMoney" direction="horizontal">
|
||||
<van-radio name="0">否</van-radio>
|
||||
<van-radio name="1">是</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field v-show="popObj.accountStatus === '1' && popObj.isMoney === '1'" name="tradeMoney"
|
||||
v-model="popObj.tradeMoney" label="交易总金额" placeholder="请输入交易总金额" />
|
||||
<van-field v-show="popObj.accountStatus === '1' && popObj.isMoney === '1'" clickable name="capitalScaleName"
|
||||
v-model="popObj.capitalScaleName" label="资金规模(亿)" placeholder="点击选择资金规模"
|
||||
@click="popObj.handleShowPop1('capitalScale', '资金规模')" readonly />
|
||||
<van-field v-show="popObj.accountStatus === '1' && popObj.isMoney === '1'" name="capitalScaleValue"
|
||||
v-model="popObj.capitalScaleValue" label="资金规模(亿)" placeholder="请输入资金规模" @blur="popObj.setAccountStatus" />
|
||||
<van-field name="fundNum" v-model="popObj.fundNum" label="基金数量" placeholder="请输入基金数量" />
|
||||
<van-field name="investOverseasPer" v-model="popObj.investOverseasPer" label="海外投资占比" placeholder="请输入海外投资占比" />
|
||||
<van-field name="investResearchNum" v-model="popObj.investResearchNum" label="投研人数" placeholder="请输入投研人数" />
|
||||
<van-field clickable name="investChannelName" v-model="popObj.investChannelName" label="投资渠道"
|
||||
placeholder="点击选择投资渠道" @click="popObj.handleShowPop1('investChannel', '投资渠道')" readonly />
|
||||
<van-field name="website" v-model="popObj.website" label="公司网站" placeholder="请输入公司网站" />
|
||||
<van-field clickable name="companyCreateTime" v-model="popObj.companyCreateTime" label="开户时间"
|
||||
placeholder="点击选择公司创建时间" @click="popObj.handleShowPop4('companyCreateTime', '公司创建时间')" readonly />
|
||||
<van-field name="introduction" v-model="popObj.introduction" label="公司简介" rows="3" type="textarea" autosize />
|
||||
<van-field clickable name="saleUserNames" v-model="popObj.saleUserNames" label="负责人" placeholder="点击选择负责人"
|
||||
@click="popObj.handleShowPop3('saleUser', '负责人')" readonly />
|
||||
<div class="m-16 d-flex justify-content-center">
|
||||
<van-button round block type="primary" size="small" native-type="submit"
|
||||
:disabled="submitFlag">提交</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import moment from 'moment'
|
||||
import emitter from '@/utils/mitt'
|
||||
import { sysDicListByType, sysUserGetSaleAndManagerList, cusUserLookUp, cusInfoSave } from '@/utils/api'
|
||||
let submitFlag = ref(false)
|
||||
|
||||
import { debounceThrottle } from '@/mixins/debounce-throttle'
|
||||
const { throttle } = debounceThrottle()
|
||||
import { type CusObj } from '@/utils/types'
|
||||
const popObj: CusObj = reactive({
|
||||
showPop1: false,
|
||||
showPop2: false,
|
||||
showPop3: false,
|
||||
showPop4: false,
|
||||
popTitle: '',
|
||||
type: '',
|
||||
value1: [],
|
||||
value3: [],
|
||||
value4: [],
|
||||
cusName: '',
|
||||
cusLevelName: '',
|
||||
selectcusLevel: '',
|
||||
cusLevelList: [],
|
||||
cusUserName: '',
|
||||
selectcusUser: '',
|
||||
searchValue: '',
|
||||
showPop2List: [],
|
||||
companyTypeName: '',
|
||||
selectcompanyType: '',
|
||||
companyTypeList: [],
|
||||
cityName: '',
|
||||
selectcity: '',
|
||||
cityList: [],
|
||||
address: '',
|
||||
accountStatus: '1',
|
||||
accountTime: '',
|
||||
selectaccountTime: [],
|
||||
accountTypeNames: '',
|
||||
selectaccountTypeIds: [],
|
||||
accountTypeList: [],
|
||||
isMoney: '0',
|
||||
tradeMoney: '',
|
||||
capitalScaleName: '',
|
||||
selectcapitalScale: '',
|
||||
capitalScaleList: [],
|
||||
capitalScaleValue: '',
|
||||
fundNum: '',
|
||||
investOverseasPer: '',
|
||||
investResearchNum: '',
|
||||
investChannelName: '',
|
||||
selectinvestChannel: '',
|
||||
investChannelList: [],
|
||||
website: '',
|
||||
companyCreateTime: '',
|
||||
selectcompanyCreateTime: [],
|
||||
introduction: '',
|
||||
saleUserNames: '',
|
||||
selectsaleUserIds: [],
|
||||
saleUserList: [],
|
||||
handleShowPop1: (val: string, title: string) => {
|
||||
popObj.showPop1 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.value1 = [popObj[`select${val}`]]
|
||||
},
|
||||
handleShowPop2: (val: string, title: string) => {
|
||||
popObj.showPop2 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.searchValue = popObj[`${val}Name`]
|
||||
popObj.showPop2List = popObj[`select${val}`] ? [{ text: popObj[`${val}Name`], value: popObj[`select${val}`] }] : []
|
||||
},
|
||||
handleShowPop3: (val: string, title: string) => {
|
||||
popObj.showPop3 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.value3 = popObj[`select${val}Ids`]
|
||||
},
|
||||
handleShowPop4: (val: string, title: string) => {
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
if (popObj[`select${val}`].length === 0) {
|
||||
let nowDay = moment().format('YYYY-MM-DD HH:mm:ss')
|
||||
popObj.value4 = nowDay.slice(0, 10).split('-')
|
||||
} else {
|
||||
popObj.value4 = popObj[`select${val}`]
|
||||
}
|
||||
popObj.showPop4 = true
|
||||
},
|
||||
onConfirm1: ({ selectedOptions }) => {
|
||||
if (selectedOptions) {
|
||||
popObj[`select${popObj.type}`] = selectedOptions[0].value
|
||||
popObj[`${popObj.type}Name`] = selectedOptions[0].text
|
||||
if (popObj.type === 'capitalScale') {
|
||||
if (popObj.capitalScale === '1') {
|
||||
if (Number(popObj.capitalScaleValue) > 10) {
|
||||
popObj.capitalScaleValue = ''
|
||||
}
|
||||
} else if (popObj.capitalScale === '2') {
|
||||
if (Number(popObj.capitalScaleValue) > 100 || Number(popObj.capitalScale) < 10) {
|
||||
popObj.capitalScaleValue = ''
|
||||
}
|
||||
} else if (popObj.capitalScale === '3') {
|
||||
if (Number(popObj.capitalScaleValue) < 100) {
|
||||
popObj.capitalScaleValue = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
popObj.showPop1 = false
|
||||
},
|
||||
onSearchInput: throttle(
|
||||
async () => {
|
||||
if (popObj.searchValue) {
|
||||
const { data } = await cusUserLookUp({ cusUserName: popObj.searchValue })
|
||||
popObj.showPop2List = data.map((ele: any) => {
|
||||
ele.text = `${ele.cusUserName}${ele.positionName ? '【' + ele.positionName + '】' : ''}`
|
||||
ele.value = ele.cusUserId
|
||||
return ele
|
||||
})
|
||||
} else {
|
||||
popObj.showPop2List = []
|
||||
}
|
||||
},
|
||||
500
|
||||
),
|
||||
onConfirm2: ({ selectedOptions }) => {
|
||||
if (selectedOptions && selectedOptions[0]) {
|
||||
popObj[`select${popObj.type}`] = selectedOptions[0].value
|
||||
popObj[`${popObj.type}Name`] = selectedOptions[0].text
|
||||
popObj.showPop2 = false
|
||||
} else {
|
||||
showToast(`请选择${popObj.popTitle}后,再确认`)
|
||||
}
|
||||
},
|
||||
onConfirm3: () => {
|
||||
popObj[`select${popObj.type}Ids`] = popObj.value3
|
||||
const sameList = popObj[`${popObj.type}List`].filter((ele: any) => popObj.value3.some((res: any) => {
|
||||
return ele.value === res
|
||||
}))
|
||||
let names = ''
|
||||
sameList.forEach((ele: any) => {
|
||||
names += `${ele.text},`
|
||||
})
|
||||
if (names) {
|
||||
names = names.substring(0, names.length - 1)
|
||||
}
|
||||
popObj[`${popObj.type}Names`] = names
|
||||
popObj.showPop3 = false
|
||||
},
|
||||
onConfirm4: ({ selectedValues }) => {
|
||||
if (selectedValues) {
|
||||
popObj[popObj.type] = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`
|
||||
popObj[`select${popObj.type}`] = selectedValues
|
||||
}
|
||||
popObj.showPop4 = false
|
||||
},
|
||||
setAccountStatus: () => {
|
||||
if (Number(popObj.capitalScaleValue) >= 0 && Number(popObj.capitalScaleValue) < 10) {
|
||||
popObj.selectcapitalScale = '1'
|
||||
} else if (Number(popObj.capitalScaleValue) >= 10 && Number(popObj.capitalScaleValue) < 100) {
|
||||
popObj.selectcapitalScale = '2'
|
||||
} else if (Number(popObj.capitalScaleValue) >= 100) {
|
||||
popObj.selectcapitalScale = '3'
|
||||
} else {
|
||||
popObj.selectcapitalScale = ''
|
||||
}
|
||||
let index = popObj.capitalScaleList.findIndex((ele: any) => {
|
||||
return ele.dicKey === popObj.selectcapitalScale
|
||||
})
|
||||
if (index > -1) {
|
||||
popObj.capitalScaleName = popObj.capitalScaleList[index].dicValue
|
||||
} else {
|
||||
popObj.capitalScaleName = ''
|
||||
}
|
||||
}
|
||||
})
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
import saveCusInfoStore from '@/stores/saveCusInfo'
|
||||
const saveCusInfo: any = saveCusInfoStore()
|
||||
import saveInfoStore from '@/stores/saveInfo'
|
||||
const saveInfo: any = saveInfoStore()
|
||||
let linkId: any = ''
|
||||
const getSelectList = async () => {
|
||||
linkId = route.query.linkId
|
||||
emitter.emit('showLoading', '')
|
||||
const data1 = await sysDicListByType({ dicType: 'cus_level' })
|
||||
popObj.cusLevelList = data1.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data2 = await sysDicListByType({ dicType: 'sale_company_type' })
|
||||
popObj.companyTypeList = data2.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data3 = await sysDicListByType({ dicType: 'cus_city' })
|
||||
popObj.cityList = data3.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data4 = await sysDicListByType({ dicType: 'account_type' })
|
||||
popObj.accountTypeList = data4.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data5 = await sysDicListByType({ dicType: 'invest_channel' })
|
||||
popObj.investChannelList = data5.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data6 = await sysUserGetSaleAndManagerList()
|
||||
popObj.saleUserList = data6.data.map((ele: any) => {
|
||||
ele.value = ele.userId
|
||||
ele.text = ele.userName
|
||||
return ele
|
||||
})
|
||||
const data7 = await sysDicListByType({ dicType: 'sale_capital_scale' })
|
||||
popObj.capitalScaleList = data7.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
for (let i in saveCusInfo) {
|
||||
if (Object.prototype.hasOwnProperty.call(popObj, i)) {
|
||||
popObj[i] = saveCusInfo[i]
|
||||
}
|
||||
}
|
||||
if (saveInfo.type === 'cusUser') {
|
||||
popObj.cusUserName = `${saveInfo.name}${saveInfo.positionName ? '【' + saveInfo.positionName + '】' : ''}`
|
||||
popObj.selectcusUser = saveInfo.id
|
||||
saveInfo.$reset()
|
||||
}
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
|
||||
const handleKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keyup', handleKeyUp)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keyup', handleKeyUp)
|
||||
})
|
||||
|
||||
const toAddCusUser = () => {
|
||||
for (let i in saveCusInfo) {
|
||||
if (Object.prototype.hasOwnProperty.call(popObj, i)) {
|
||||
saveCusInfo[i] = popObj[i]
|
||||
}
|
||||
}
|
||||
if (linkId) {
|
||||
router.push({ name: 'add-cusUser', query: { linkId: linkId, from: 'cus' } })
|
||||
} else {
|
||||
router.push({ name: 'add-cusUser', query: { linkId: linkId, from: 'cus' } })
|
||||
}
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
submitFlag.value = true
|
||||
const res: any = {
|
||||
cusName: popObj.cusName,
|
||||
cusLevel: popObj.selectcusLevel || 'E',
|
||||
cusUserId: popObj.selectcusUser || null,
|
||||
cusUserIdList: popObj.selectcusUser ? [popObj.selectcusUser] : [],
|
||||
companyType: popObj.selectcompanyType || null,
|
||||
city: popObj.selectcity || null,
|
||||
address: popObj.address,
|
||||
accountStatus: popObj.accountStatus,
|
||||
accountTypeList: popObj.accountStatus === '1' && popObj.selectaccountTypeIds.length > 0 ?
|
||||
popObj.selectaccountTypeIds : null,
|
||||
accountTime: popObj.accountStatus === '1' && popObj.accountTime ?
|
||||
popObj.accountTime : null,
|
||||
tradeMoney: popObj.accountStatus === '1' && (popObj.tradeMoney === '1' || popObj.tradeMoney === '0') ?
|
||||
popObj.tradeMoney : null,
|
||||
capitalScaleValue: popObj.capitalScaleValue || null,
|
||||
capitalScale: popObj.selectcapitalScale || null,
|
||||
fundNum: popObj.fundNum || null,
|
||||
investOverseasPer: popObj.investOverseasPer || null,
|
||||
investResearchNum: popObj.investResearchNum || null,
|
||||
investChannel: popObj.selectinvestChannel || null,
|
||||
website: popObj.website || null,
|
||||
companyCreateTime: popObj.companyCreateTime || null,
|
||||
introduction: popObj.introduction || null,
|
||||
saleUserIdList: popObj.selectsaleUserIds || null
|
||||
}
|
||||
const data: any = await cusInfoSave(res)
|
||||
if (data.code === 0) {
|
||||
showToast('提交成功')
|
||||
saveInfo.id = data.data.cusId
|
||||
saveInfo.name = data.data.cusName
|
||||
saveInfo.type = 'cus'
|
||||
saveCusInfo.$reset()
|
||||
router.go(-1)
|
||||
} else {
|
||||
showToast(data.msg)
|
||||
submitFlag.value = false
|
||||
}
|
||||
}
|
||||
|
||||
getSelectList()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.add-cusUser-detail {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detailClass {
|
||||
.van-cell {
|
||||
margin: 12px 16px 0;
|
||||
width: calc(100vw - 32px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
width: 646px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-field__control) {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
:deep(.van-checkbox) {
|
||||
margin-right: 0 !important;
|
||||
width: 50%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
222
src/views/communicate/add-cusUser.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="add-cus-detail detailClass">
|
||||
<van-form @submit="onSubmit" validate-trigger="onSubmit">
|
||||
<van-field name="cusUserName" v-model="popObj.cusUserName" label="联系人" placeholder="请输入联系人" :required="true"
|
||||
:rules="[{ required: true, message: '请填写联系人' }]" />
|
||||
<van-field clickable name="positionName" v-model="popObj.positionName" label="职位" placeholder="点击选择职位"
|
||||
@click="popObj.handleShowPop1('position', '职位')" readonly :required="true"
|
||||
:rules="[{ required: true, message: '请填写职位' }]" />
|
||||
<van-popup v-model:show="popObj.showPop1" position="bottom">
|
||||
<van-picker show-toolbar :title="popObj.popTitle" :columns="popObj[`${popObj.type}List`]"
|
||||
@confirm="popObj.onConfirm1($event)" @cancel="popObj.showPop1 = false" v-model="popObj.value1" />
|
||||
</van-popup>
|
||||
<van-field clickable name="saleUserNames" v-model="popObj.saleUserNames" label="跟进人" placeholder="点击选择跟进人"
|
||||
@click="popObj.handleShowPop3('saleUser', '跟进人')" readonly />
|
||||
<van-popup v-model:show="popObj.showPop3" position="bottom">
|
||||
<div class="p-16 box-popup">
|
||||
<van-row class="mb-16">
|
||||
<van-col span="8">
|
||||
<van-button class="van-picker__cancel" native-type="button"
|
||||
@click="popObj.showPop3 = false">取消</van-button>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-center">
|
||||
<span class="sub-title f-b">请选择{{ popObj.popTitle }}</span>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-end">
|
||||
<van-button class="van-picker__confirm" native-type="button" @click="popObj.onConfirm3">确认</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<van-checkbox-group v-model="popObj.value3" direction="horizontal">
|
||||
<van-checkbox v-for="(item, index) in popObj[`${popObj.type}List`]" :key="index" :name="item.value"
|
||||
shape="square">
|
||||
{{ item.text }}
|
||||
</van-checkbox>
|
||||
</van-checkbox-group>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-field name="phone" v-model="popObj.phone" label="座机号" placeholder="请输入座机号" />
|
||||
<van-field name="mobile" v-model="popObj.mobile" label="手机号" placeholder="请输入手机号" />
|
||||
<van-field name="email" v-model="popObj.email" label="邮箱" placeholder="请输入邮箱" />
|
||||
<van-field name="wxName" v-model="popObj.wxName" label="微信名" placeholder="请输入微信名" />
|
||||
<van-field name="address" v-model="popObj.address" label="地址" placeholder="请输入地址" />
|
||||
<van-field name="preference" v-model="popObj.preference" label="擅长" placeholder="请输入擅长" />
|
||||
<div class="m-16 d-flex justify-content-center">
|
||||
<van-button round block type="primary" size="small" native-type="submit"
|
||||
:disabled="submitFlag">提交</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import emitter from '@/utils/mitt'
|
||||
import { cusUserCusPositionList, sysUserGetSaleAndManagerList, cusUserSave } from '@/utils/api'
|
||||
let submitFlag = ref(false)
|
||||
|
||||
import { type CusUserObj } from '@/utils/types'
|
||||
const popObj: CusUserObj = reactive({
|
||||
showPop1: false,
|
||||
showPop3: false,
|
||||
popTitle: '',
|
||||
type: '',
|
||||
value1: [],
|
||||
value3: [],
|
||||
cusUserName: '',
|
||||
positionName: '',
|
||||
selectposition: '',
|
||||
positionList: [],
|
||||
saleUserNames: '',
|
||||
selectsaleUserIds: [],
|
||||
saleUserList: [],
|
||||
selectcusUser: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
email: '',
|
||||
wxName: '',
|
||||
address: '',
|
||||
preference: '',
|
||||
handleShowPop1: (val: string, title: string) => {
|
||||
popObj.showPop1 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.value1 = [popObj[`select${val}`]]
|
||||
},
|
||||
handleShowPop3: (val: string, title: string) => {
|
||||
popObj.showPop3 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.value3 = popObj[`select${val}Ids`]
|
||||
},
|
||||
onConfirm1: ({ selectedOptions }) => {
|
||||
if (selectedOptions) {
|
||||
popObj[`select${popObj.type}`] = selectedOptions[0].value
|
||||
popObj[`${popObj.type}Name`] = selectedOptions[0].text
|
||||
}
|
||||
popObj.showPop1 = false
|
||||
},
|
||||
onConfirm3: () => {
|
||||
popObj[`select${popObj.type}Ids`] = popObj.value3
|
||||
const sameList = popObj[`${popObj.type}List`].filter((ele: any) => popObj.value3.some((res: any) => {
|
||||
return ele.value === res
|
||||
}))
|
||||
let names = ''
|
||||
sameList.forEach((ele: any) => {
|
||||
names += `${ele.text},`
|
||||
})
|
||||
if (names) {
|
||||
names = names.substring(0, names.length - 1)
|
||||
}
|
||||
popObj[`${popObj.type}Names`] = names
|
||||
popObj.showPop3 = false
|
||||
}
|
||||
})
|
||||
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
let cusId: any = ''
|
||||
let linkId: any = ''
|
||||
let from: any = ''
|
||||
const getSelectList = async () => {
|
||||
if (route.query && route.query.from) {
|
||||
cusId = route.query && route.query.cusId ? route.query.cusId : ''
|
||||
linkId = route.query && route.query.linkId ? route.query.linkId : ''
|
||||
from = route.query.from
|
||||
emitter.emit('showLoading', '')
|
||||
const data1 = await cusUserCusPositionList()
|
||||
popObj.positionList = data1.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data2 = await sysUserGetSaleAndManagerList()
|
||||
popObj.saleUserList = data2.data.map((ele: any) => {
|
||||
ele.value = ele.userId
|
||||
ele.text = ele.userName
|
||||
return ele
|
||||
})
|
||||
emitter.emit('hiddenLoading', '')
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keyup', handleKeyUp)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keyup', handleKeyUp)
|
||||
})
|
||||
|
||||
import saveInfoStore from '@/stores/saveInfo'
|
||||
const saveInfo: any = saveInfoStore()
|
||||
const onSubmit = async () => {
|
||||
submitFlag.value = true
|
||||
const res: any = {
|
||||
cusUserName: popObj.cusUserName,
|
||||
positionId: popObj.selectposition || null,
|
||||
saleUserIdList: popObj.selectsaleUserIds || null,
|
||||
phone: popObj.phone || null,
|
||||
mobile: popObj.mobile || null,
|
||||
email: popObj.email || null,
|
||||
wxName: popObj.wxName || null,
|
||||
address: popObj.address || null,
|
||||
preference: popObj.preference || null
|
||||
}
|
||||
if (cusId) {
|
||||
res.cusId = cusId
|
||||
}
|
||||
const data: any = await cusUserSave(res)
|
||||
if (data.code === 0) {
|
||||
showToast('提交成功')
|
||||
saveInfo.id = data.data.cusUserId
|
||||
saveInfo.name = data.data.cusUserName
|
||||
saveInfo.positionName = data.data.positionName
|
||||
saveInfo.type = 'cusUser'
|
||||
router.go(-1)
|
||||
} else {
|
||||
submitFlag.value = false
|
||||
}
|
||||
}
|
||||
|
||||
getSelectList()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.add-cus-detail {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detailClass {
|
||||
.van-cell {
|
||||
margin: 12px 16px 0;
|
||||
width: calc(100vw - 32px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
width: 646px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-field__control) {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
:deep(.van-checkbox) {
|
||||
margin-right: 0 !important;
|
||||
width: 50%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
1038
src/views/communicate/detail.vue
Normal file
254
src/views/communicate/list.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="page-content top-select-list" ref="pageContent">
|
||||
<div v-if="userInfo.roleId === '1' || userInfo.roleId === '9' || userInfo.roleId === '10' || userInfo.roleId === '6'
|
||||
|| userInfo.roleId === '7' || userInfo.roleId === '16'" class="addDiv success-background-color"
|
||||
@click="$router.push({ name: 'communicate-detail', query: { addDay: selectDay } })">
|
||||
<div class="text-center addDiv1">新增</div>
|
||||
<div class="text-center addDiv2">沟通</div>
|
||||
</div>
|
||||
<div class="top-select border-bottom-1">
|
||||
<van-dropdown-menu>
|
||||
<van-dropdown-item :title="cusName" ref="itemRef">
|
||||
<van-field v-model="searchValue" placeholder="请输入机构" @input="onSearchInput"
|
||||
:right-icon="searchValue ? 'clear' : ''" @click-right-icon.stop="clearSearch" />
|
||||
<van-picker show-toolbar title="机构列表" :columns="list" @confirm="onConfirm" @cancel="onCancel" />
|
||||
</van-dropdown-item>
|
||||
<van-dropdown-item v-model="linkType" :options="linkTypeList" @change="getLinkList" />
|
||||
<van-dropdown-item v-model="saleId" :options="saleList" @change="getLinkList" />
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
<van-calendar title="" :poppable="false" :show-confirm="false" switch-mode="year-month" first-day-of-week="1"
|
||||
:style="{ height: '410px' }" :formatter="formatter" :default-date="defaultDate" @panel-change="panelChange"
|
||||
@select="onDateConfirm" />
|
||||
<communicate-item class="pb-16" :list="list" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
import { convertToUrl } from '@/utils'
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
const userInfo = userInfoStore()
|
||||
|
||||
import { cusLinkInfo, cusLinkList, sysUserGetSaleAndManagerList, cusInfoLookUp } from '@/utils/api'
|
||||
let cusName = ref('机构')
|
||||
let cusId = ref('')
|
||||
let searchValue = ref('')
|
||||
let itemRef = ref(null)
|
||||
import { debounceThrottle } from '@/mixins/debounce-throttle'
|
||||
const { throttle } = debounceThrottle()
|
||||
const onSearchInput = throttle(
|
||||
async () => {
|
||||
if (searchValue.value) {
|
||||
const { data } = await cusInfoLookUp({ cusName: searchValue.value })
|
||||
list.value = data.map((ele: any) => {
|
||||
ele.text = ele.cusName
|
||||
ele.value = ele.cusId
|
||||
return ele
|
||||
})
|
||||
} else {
|
||||
list.value = []
|
||||
}
|
||||
},
|
||||
500
|
||||
)
|
||||
|
||||
const clearSearch = () => {
|
||||
searchValue.value = ''
|
||||
}
|
||||
|
||||
const onConfirm = (e: any) => {
|
||||
console.log(e)
|
||||
if (e.selectedValues.length > 0) {
|
||||
cusName.value = e.selectedOptions[0].cusName
|
||||
cusId.value = e.selectedOptions[0].cusId
|
||||
searchValue.value = e.selectedOptions[0].cusName
|
||||
getLinkList()
|
||||
}
|
||||
itemRef.value.toggle()
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
itemRef.value.toggle()
|
||||
}
|
||||
|
||||
let linkType = ref('31,32,34')
|
||||
let linkTypeList: any = ref([{
|
||||
text: '全部类型',
|
||||
value: '31,32,34'
|
||||
}, {
|
||||
text: '上市公司按需会议',
|
||||
value: '31'
|
||||
}, {
|
||||
text: '机构路演',
|
||||
value: '32'
|
||||
}, {
|
||||
text: '机构沟通',
|
||||
value: '34'
|
||||
}])
|
||||
|
||||
let saleId = ref('')
|
||||
let saleList: any = ref([])
|
||||
const getSaleList = async () => {
|
||||
const { data } = await sysUserGetSaleAndManagerList()
|
||||
saleList.value = data.map((ele: any) => {
|
||||
ele.text = ele.userName
|
||||
ele.value = ele.userId
|
||||
return ele
|
||||
})
|
||||
saleList.value.unshift({ text: '全部负责人', value: '' })
|
||||
}
|
||||
getSaleList()
|
||||
|
||||
let defaultDate = ref()
|
||||
import moment from 'moment'
|
||||
const panelChange = (e: any) => {
|
||||
dateBegin.value = moment(e.date).startOf('month').format('YYYY-MM-DD')
|
||||
dateEnd.value = moment(e.date).endOf('month').format('YYYY-MM-DD')
|
||||
getLinkList()
|
||||
}
|
||||
|
||||
const onDateConfirm = (e: any) => {
|
||||
defaultDate.value = e
|
||||
getList()
|
||||
}
|
||||
|
||||
let list: any = ref([])
|
||||
let selectDay = ref('')
|
||||
const getList = () => {
|
||||
list.value = []
|
||||
selectDay.value = moment(defaultDate.value).format('YYYY-MM-DD')
|
||||
linkList.value.forEach((ele: any) => {
|
||||
if (ele.sysDay === selectDay.value) {
|
||||
list.value = ele.list
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formatter = computed(() => {
|
||||
if (linkList.value.length === 0) {
|
||||
return (day: any) => day
|
||||
}
|
||||
return (day: any) => {
|
||||
let time = moment(day.date).format('YYYY-MM-DD')
|
||||
linkList.value.forEach((ele: any) => {
|
||||
if (ele.sysDay === time) {
|
||||
day.topInfo = ele.num
|
||||
}
|
||||
})
|
||||
return day
|
||||
}
|
||||
})
|
||||
|
||||
const dateBegin = ref()
|
||||
const dateEnd = ref()
|
||||
const linkList: any = ref([])
|
||||
const getLinkList = async () => {
|
||||
let url = getLinkParams()
|
||||
const data = await cusLinkList(url)
|
||||
let map = new Map()
|
||||
data.forEach((item: any, index: any, arr: any) => {
|
||||
if (!map.has(item.sysDay)) {
|
||||
map.set(
|
||||
item.sysDay,
|
||||
arr.filter((a: any) => a.sysDay == item.sysDay)
|
||||
)
|
||||
}
|
||||
})
|
||||
let res = Array.from(map).map((item) => [...item[1]])
|
||||
linkList.value = []
|
||||
res.forEach((ele: any) => {
|
||||
linkList.value.push({ sysDay: ele[0].sysDay, num: ele.length, list: ele })
|
||||
})
|
||||
getList()
|
||||
}
|
||||
|
||||
const getLinkParams = () => {
|
||||
const res = {
|
||||
dateBegin: dateBegin.value,
|
||||
dateEnd: dateEnd.value,
|
||||
limit: 999,
|
||||
dataStatus: 1,
|
||||
curPage: 1,
|
||||
saleId: saleId.value,
|
||||
linkTypes: linkType.value,
|
||||
cusId: cusId.value
|
||||
}
|
||||
const url = convertToUrl(res)
|
||||
return url
|
||||
}
|
||||
|
||||
let pageContent = ref(null)
|
||||
const scrollPosition = ref(0)
|
||||
const refresh = async (type: any, id: any) => {
|
||||
if (type === 'delete' || type === 'refresh') {
|
||||
let dayIndex = linkList.value.findIndex((ele: any) => {
|
||||
return ele.sysDay === selectDay.value
|
||||
})
|
||||
if (dayIndex > -1) {
|
||||
let index = linkList.value[dayIndex].list.findIndex((ele: any) => {
|
||||
return Number(ele.linkId) === Number(id)
|
||||
})
|
||||
if (type === 'delete') {
|
||||
if (index > -1) {
|
||||
linkList.value[dayIndex].list.splice(index, 1)
|
||||
linkList.value[dayIndex].num -= 1
|
||||
}
|
||||
} else {
|
||||
if (index > -1) {
|
||||
const { data } = await cusLinkInfo(id)
|
||||
switch (data.linkTypeMenu) {
|
||||
case '32':
|
||||
data.tagColor = 'danger-background-color'
|
||||
break
|
||||
case '34':
|
||||
data.tagColor = 'primary-background-color'
|
||||
break
|
||||
case '31':
|
||||
data.tagColor = 'success-background-color'
|
||||
break
|
||||
}
|
||||
linkList.value[dayIndex].list[index] = data
|
||||
}
|
||||
getList()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defaultDate.value = new Date()
|
||||
dateBegin.value = moment().startOf('month').format('YYYY-MM-DD')
|
||||
dateEnd.value = moment().endOf('month').format('YYYY-MM-DD')
|
||||
getLinkList()
|
||||
}
|
||||
}
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, refresh)
|
||||
setScrollTop()
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'communicate-detail')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
:deep(.van-calendar__header-title) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.van-calendar__top-info) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: calc(100vw / 7 - 20px);
|
||||
border-radius: 100%;
|
||||
background-color: #f56c6c;
|
||||
color: #fff;
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
left: 58px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-calendar__day) {
|
||||
border: 1px solid #f2f2f2;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
90
src/views/flow/company.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="tabbar-list" ref="pageContent">
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" v-if="list.length > 0">
|
||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"
|
||||
:immediate-check="false">
|
||||
<van-grid :border="false" :clickable="true" :column-num="3">
|
||||
<van-grid-item v-for="item in list" :key="item.id"
|
||||
@click="$router.push({ name: 'flow', query: { stockCode: item.stockCode } })">
|
||||
<div class="itemImage">
|
||||
<svg class="stockIcon" aria-hidden="true">
|
||||
<use :xlink:href="`#icon-${item.companyIcon}`"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="itemName">{{ item.stockName }}</div>
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
import { stockCompanyFlowInfoListCompany } from '@/utils/api'
|
||||
let pageContent = ref(null)
|
||||
const scrollPosition = ref(0)
|
||||
const getData = async () => {
|
||||
const { data } = await stockCompanyFlowInfoListCompany()
|
||||
return { list: data, totalCount: data.length }
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
onRefresh()
|
||||
}
|
||||
import { listLoadAndRefresh } from '@/mixins/list-load-and-refresh'
|
||||
const { refreshing, finished, loading, list, onLoad, onRefresh } = listLoadAndRefresh(getData, 'stock')
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, refresh)
|
||||
|
||||
setScrollTop()
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'track')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.tabbar-list {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
:deep(.van-grid-item) {
|
||||
padding-right: 20px;
|
||||
padding-bottom: 26px;
|
||||
}
|
||||
|
||||
:deep(.van-grid-item:nth-child(3n)) {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
:deep(.van-grid-item__content) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.itemImage {
|
||||
margin-top: 18px;
|
||||
height: 36px;
|
||||
|
||||
.stockIcon {
|
||||
max-height: 36px;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.itemName {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
47
src/views/flow/flow-detail.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="page-content p-10">
|
||||
<div class="d-flex justify-content-between">
|
||||
<van-tag type="primary" size="large">{{ flowData.followType }}</van-tag>
|
||||
<div>{{ flowData.sysDate }}</div>
|
||||
</div>
|
||||
<div class="mt-10" v-if="flowData.fileId">
|
||||
<audio :id="`auto${flowData.id}`" :src="flowData.fileUrl" controls></audio>
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
{{ flowData.introduce }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import emitter from '@/utils/mitt'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
let id: any = ref('')
|
||||
let flowData: any = ref({})
|
||||
const init = () => {
|
||||
if (route.query && route.query.id) {
|
||||
id.value = route.query.id
|
||||
emitter.emit('showLoading', '')
|
||||
getData()
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
import { stockCompanyFlowInfoInfo } from '@/utils/api'
|
||||
const getData = async () => {
|
||||
const { data }: any = await stockCompanyFlowInfoInfo(id.value)
|
||||
data.playFlag = false
|
||||
flowData.value = data
|
||||
emitter.emit('setTitle', { title: data.sysDate + data.stockName + data.flowType, type: 'comment' })
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
audio {
|
||||
height: 36px;
|
||||
}
|
||||
</style>
|
||||
60
src/views/flow/flow.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" v-if="list.length > 0">
|
||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"
|
||||
:immediate-check="false">
|
||||
<van-cell v-for="item in list" :key="item.id"
|
||||
@click="$router.push({ name: 'flow-detail', query: { id: item.id } })">
|
||||
<div class="text-start"><van-tag type="primary" size="large">{{ item.followType }}</van-tag></div>
|
||||
<van-text-ellipsis class="text-start mt-10" :content="item.introduce" rows="3" expand-text="展开"
|
||||
collapse-text="收起" @click-action="showOrHiddenText" />
|
||||
<div>{{ item.sysDate }}</div>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'
|
||||
let pageContent = ref(null)
|
||||
const scrollPosition = ref(0)
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
let stockCode: any = ref('')
|
||||
const refresh = () => {
|
||||
if (route.query && route.query.stockCode) {
|
||||
stockCode.value = route.query.stockCode
|
||||
onRefresh()
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
import { stockCompanyFlowInfoList } from '@/utils/api'
|
||||
const getData = async () => {
|
||||
const { data }: any = await stockCompanyFlowInfoList({
|
||||
stockCode: stockCode.value,
|
||||
curPage: curPage.value,
|
||||
limit: 20
|
||||
})
|
||||
data.list.map((ele: any) => {
|
||||
ele.playFlag = false
|
||||
})
|
||||
return data
|
||||
}
|
||||
const showOrHiddenText = (event: any) => {
|
||||
event.stopPropagation()
|
||||
}
|
||||
import { listLoadAndRefresh } from '@/mixins/list-load-and-refresh'
|
||||
const { refreshing, finished, loading, curPage, list, onLoad, onRefresh } = listLoadAndRefresh(getData, 'stock')
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, refresh)
|
||||
|
||||
setScrollTop()
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'track-detail')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped></style>
|
||||
202
src/views/home/index.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="page-content tabbar-list">
|
||||
<div class="home-top" :class="isNight ? 'night' : ''">
|
||||
<div class="title">{{ tips }}</div>
|
||||
<div class="top-content">
|
||||
<van-image class="banner" src="/img/home-banner.jpg" alt="" />
|
||||
<div class="name">第一上海</div>
|
||||
<div class="welcome">数据管理系统欢迎您</div>
|
||||
</div>
|
||||
</div>
|
||||
<van-grid :column-num="4" :border=false>
|
||||
<van-grid-item v-if="userInfo.roleId === '10' || userInfo.roleId === '1'" class="btn"
|
||||
@click="$router.push({ name: 'organization-list' })">
|
||||
<div class="icon-box organization"><van-icon name="newspaper-o" size="24" /></div>
|
||||
<span>机构</span>
|
||||
</van-grid-item>
|
||||
<van-grid-item class="btn" @click="$router.push({ name: 'meeting-list' })">
|
||||
<div class="icon-box meeting"><van-icon name="calendar-o" size="24" /></div>
|
||||
<span>会议</span>
|
||||
</van-grid-item>
|
||||
<van-grid-item class="btn" @click="$router.push({ name: 'communicate-list' })">
|
||||
<div class="icon-box communicate"><van-icon name="comment-o" size="24" /></div>
|
||||
<span>沟通</span>
|
||||
</van-grid-item>
|
||||
<van-grid-item class="btn" @click="$router.push({ name: 'comment-list' })">
|
||||
<div class="icon-box comment"><van-icon name="diamond-o" size="24" /></div>
|
||||
<span>点评</span>
|
||||
</van-grid-item>
|
||||
<van-grid-item class="btn" @click="$router.push({ name: 'report-list' })">
|
||||
<div class="icon-box report"><van-icon name="description-o" size="24" /></div>
|
||||
<span>报告</span>
|
||||
</van-grid-item>
|
||||
<van-grid-item class="btn" @click="$router.push({ name: 'target-table' })">
|
||||
<div class="icon-box cash"><van-icon name="guide-o" size="24" /></div>
|
||||
<span>指标</span>
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
<div class="card">
|
||||
<div class="card-title">今日沟通</div>
|
||||
<communicate-item :list="communicateList" />
|
||||
</div>
|
||||
<tabbar />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
const userInfo = userInfoStore()
|
||||
const time = moment().hour()
|
||||
const isNight = computed(() => time < 6 || time >= 22)
|
||||
const tips = computed(() => {
|
||||
if (time < 6 || time >= 22) {
|
||||
return '夜深了,早点休息'
|
||||
} else if (time >= 19) {
|
||||
return `晚上好,${userInfo.realname}`
|
||||
} else if (time >= 12) {
|
||||
return `下午好,${userInfo.realname}`
|
||||
} else {
|
||||
return `早上好,${userInfo.realname}`
|
||||
}
|
||||
})
|
||||
import { cusLinkList } from '@/utils/api'
|
||||
import { convertToUrl } from '@/utils'
|
||||
import moment from 'moment'
|
||||
const isLogin = computed(() => !!userInfo.token)
|
||||
const communicateList = ref([])
|
||||
const getCusLink = async () => {
|
||||
if (!isLogin.value) return
|
||||
const url = convertToUrl({
|
||||
limit: 3,
|
||||
curPage: 1,
|
||||
dateBegin: moment().format('YYYY-MM-DD')
|
||||
})
|
||||
const data = await cusLinkList(url)
|
||||
communicateList.value = data
|
||||
}
|
||||
getCusLink()
|
||||
import { workMeetingList } from '@/utils/api'
|
||||
const meetingList = ref<Array<any>>([])
|
||||
const getMeetingList = async () => {
|
||||
if (!isLogin.value) return
|
||||
const url = convertToUrl({
|
||||
dateBegin: moment().format('YYYY-MM-DD'),
|
||||
dateEnd: moment().format('YYYY-MM-DD'),
|
||||
meetingType: '1,2,3,16,17,4,5,15,6,10,11,12,13,14,7'
|
||||
})
|
||||
const { data } = await workMeetingList(url)
|
||||
meetingList.value = data.slice(0, 3)
|
||||
}
|
||||
getMeetingList()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.night {
|
||||
background-image: url('/img/night.png');
|
||||
background-repeat: no-repeat;
|
||||
padding-top: 30px;
|
||||
color: #fff;
|
||||
}
|
||||
.home-top {
|
||||
padding: 20px 10px 0;
|
||||
.top-content {
|
||||
height: 120px;
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
@media screen and (min-width: 678px) {
|
||||
height: 160px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
font-size: 16px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.name {
|
||||
padding-top: 30px;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
margin-left: 20px;
|
||||
}
|
||||
.welcome {
|
||||
font-size: 14px;
|
||||
color: #eee;
|
||||
margin-left: 40px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.banner {
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
}
|
||||
.btn {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s ease;
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.icon-box {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.organization {
|
||||
background-color: #ff8f00;
|
||||
}
|
||||
.meeting {
|
||||
background-color: #a3cf7d;
|
||||
}
|
||||
.communicate {
|
||||
background-color: #ffc700;
|
||||
padding-top: 2px;
|
||||
}
|
||||
.comment {
|
||||
background-color: #ff758f;
|
||||
}
|
||||
.report {
|
||||
background-color: #5bb1ff;
|
||||
}
|
||||
.cash {
|
||||
background-color: #f15b6c;
|
||||
}
|
||||
}
|
||||
.card {
|
||||
padding: 10px 0;
|
||||
// margin-top: 10px;
|
||||
margin: 10px 12px;
|
||||
.card-title {
|
||||
margin-left: 16px;
|
||||
padding: 0 4px;
|
||||
font-weight: 600;
|
||||
border-left: 4px solid #5bb1ff;
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.empty {
|
||||
color: #808080;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
:deep(.van-empty__image) {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
</style>
|
||||
135
src/views/internal/meeting.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="tabbar-list top-select-list" ref="pageContent">
|
||||
<div class="top-select border-bottom-1">
|
||||
<van-grid :border="false" :gutter="10" :column-num="2">
|
||||
<van-grid-item>
|
||||
<top-select1 :type="'speak_type'" :initialName="'类型'" @refresh="refreshBySpeakType"
|
||||
:selectName="speakTypeName" :selectValue="speakType" />
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
</div>
|
||||
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" v-if="list.length > 0">
|
||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"
|
||||
:immediate-check="false">
|
||||
<van-cell v-for="(item, index) in list" :key="item.speakId" @click="showDetail(item, index)"
|
||||
:id="item.speakId">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="name1 text-start ">
|
||||
<van-tag :class="item.tagColor" size="medium">{{ item.speakTypeName }}</van-tag>
|
||||
</div>
|
||||
<div class="name1 stockNames balck-text-color">{{ item.sysDate }}</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick, onUnmounted } from 'vue'
|
||||
import TopSelect1 from '@/components/top-select1.vue'
|
||||
import emitter from '@/utils/mitt'
|
||||
import { workSpeakList } from '@/utils/api'
|
||||
|
||||
import speakInfoStore from '@/stores/speakInfo'
|
||||
const speakInfo = speakInfoStore()
|
||||
|
||||
import { listLoadAndRefresh } from '@/mixins/list-load-and-refresh'
|
||||
const scrollPage = () => {
|
||||
nextTick(() => {
|
||||
if (speakInfo.speakId && speakInfo.index) {
|
||||
speakTypeName.value = speakInfo.speakTypeName
|
||||
speakType.value = speakInfo.speakType
|
||||
let pageIndex1 = speakInfo.index / 20
|
||||
let pageIndex2 = speakInfo.index % 20
|
||||
if (pageIndex2 > 1) {
|
||||
pageIndex1++
|
||||
}
|
||||
if (pageIndex1 > curPage.value) {
|
||||
onLoad()
|
||||
} else {
|
||||
const el = document.getElementById(speakInfo.speakId)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
speakInfo.speakId = ''
|
||||
speakInfo.index = 0
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
} else {
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getData = async () => {
|
||||
const { data } = await workSpeakList({
|
||||
curPage: curPage.value,
|
||||
limit: 20,
|
||||
speakType: speakType.value || null
|
||||
})
|
||||
data.list.map((ele: any) => {
|
||||
switch (ele.speakType) {
|
||||
case 'CH':
|
||||
ele.tagColor = 'primary-background-color'
|
||||
break
|
||||
case 'CF':
|
||||
ele.tagColor = 'danger-background-color'
|
||||
break
|
||||
case 'YJZH':
|
||||
ele.tagColor = 'success-background-color'
|
||||
break
|
||||
case 'SALE':
|
||||
ele.tagColor = 'warning-background-color'
|
||||
break
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
const { refreshing, finished, loading, list, onLoad, onRefresh, curPage } = listLoadAndRefresh(getData, 'internal', scrollPage)
|
||||
|
||||
let speakTypeName = ref('类型')
|
||||
let speakType = ref('')
|
||||
|
||||
const refreshBySpeakType = (e: any) => {
|
||||
speakType.value = e.value
|
||||
speakTypeName.value = e.name
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
import { showFile } from '@/mixins/show-file'
|
||||
const { loadingFile } = showFile()
|
||||
const showDetail = (item: any, index: number) => {
|
||||
speakInfo.index = index
|
||||
speakInfo.speakId = item.speakId
|
||||
speakInfo.speakTypeName = speakTypeName.value
|
||||
speakInfo.speakType = speakType.value
|
||||
loadingFile(item.tencentRecordUrl)
|
||||
}
|
||||
|
||||
const onRefresh1 = () => {
|
||||
speakTypeName.value = speakInfo.speakTypeName
|
||||
speakType.value = speakInfo.speakType
|
||||
onRefresh()
|
||||
|
||||
}
|
||||
onRefresh1()
|
||||
|
||||
onUnmounted(() => {
|
||||
speakInfo.speakTypeName = '类型'
|
||||
speakInfo.speakType = ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.grid-item-text {
|
||||
max-width: calc(50vw - 28px) !important;
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
max-width: 310px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
683
src/views/meeting/add-or-update-meeting.vue
Normal file
@@ -0,0 +1,683 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="add-meeting" :class="disabledFlag ? 'detailClass2' : 'detailClass1'">
|
||||
<van-form @submit="onSubmit" validate-trigger="onSubmit">
|
||||
<van-field name="meetingTitle" v-model="popObj.meetingTitle" label="会议标题"
|
||||
:placeholder="disabledFlag ? '' : '请输入会议标题'" :required="true"
|
||||
:rules="[{ required: true, message: '请填写会议标题' }]" :disabled="disabledFlag" />
|
||||
<van-field clickable name="sysDay" v-model="popObj.sysDay" label="会议日期"
|
||||
:placeholder="disabledFlag ? '' : '点击选择日期'" @click="popObj.handleShowPop4('sysDay', '选择日期', 4)"
|
||||
:required="true" :rules="[{ required: true, message: '请选择会议日期' }]" readonly :disabled="disabledFlag" />
|
||||
<van-popup v-model:show="popObj.showPop4" position="bottom">
|
||||
<van-date-picker v-model="popObj.value4" :title="popObj.popTitle" @confirm="popObj.onConfirm4($event)"
|
||||
@cancel="popObj.showPop4 = false" />
|
||||
</van-popup>
|
||||
<van-field clickable name="sysTime" v-model="popObj.sysTime" label="会议时间"
|
||||
:placeholder="disabledFlag ? '' : '点击选择时间'" @click="popObj.handleShowPop4('sysTime', '会议时间', 5)"
|
||||
:required="true" :rules="[{ required: true, message: '请选择会议时间' }]" readonly :disabled="disabledFlag" />
|
||||
<van-popup v-model:show="popObj.showPop5" position="bottom">
|
||||
<van-time-picker v-model="popObj.value5" :title="popObj.popTitle" @confirm="popObj.onConfirm4($event)"
|
||||
@cancel="popObj.showPop5 = false" />
|
||||
</van-popup>
|
||||
<van-field clickable name="meetingTypeName" v-model="popObj.meetingTypeName" label="会议类型"
|
||||
:placeholder="disabledFlag ? '' : '点击选择会议类型'" @click="popObj.handleShowPop1('meetingType', '会议类型')"
|
||||
:required="true" :rules="[{ required: true, message: '请选择会议类型' }]" readonly :disabled="disabledFlag" />
|
||||
<van-popup v-model:show="popObj.showPop1" position="bottom">
|
||||
<van-picker show-toolbar :title="popObj.popTitle" :columns="popObj[`${popObj.type}List`]"
|
||||
@confirm="popObj.onConfirm1($event)" @cancel="popObj.showPop1 = false" v-model="popObj.value1" />
|
||||
</van-popup>
|
||||
<van-field clickable name="meetingByName" v-model="popObj.meetingByName" label="会议形式"
|
||||
:placeholder="disabledFlag ? '' : '点击选择会议形式'" @click="popObj.handleShowPop1('meetingBy', '会议形式')"
|
||||
:required="true" :rules="[{ required: true, message: '请选择会议形式' }]" readonly :disabled="disabledFlag" />
|
||||
<div v-for="(item, index) in popObj.stockCodeList" :key="index">
|
||||
<van-field clickable name="text" v-model="item.text" :label="`股票代码${index + 1}`"
|
||||
:placeholder="disabledFlag ? '' : '点击选择股票'" @click="popObj.handleShowPop6('stockCode', '股票', index)"
|
||||
type="text" autosize readonly :right-icon="disabledFlag ? '' : 'close'"
|
||||
@click-right-icon.stop="popObj.deleteItemByType('stockCode', index)" :disabled="disabledFlag" />
|
||||
</div>
|
||||
<van-field clickable name="newItem" v-model="newItem" label="股票代码" placeholder="点击新增股票"
|
||||
@click="popObj.handleShowPop6('stockCode', '股票', -1)" readonly v-show="!disabledFlag" />
|
||||
<van-popup v-model:show="popObj.showPop2" position="bottom">
|
||||
<van-field v-model="popObj.searchValue" :placeholder="'请输入' + popObj.popTitle"
|
||||
@input="popObj.onSearchInput" />
|
||||
<van-picker show-toolbar :title="popObj.popTitle" :columns="popObj.showPop2List"
|
||||
@confirm="popObj.onConfirm2($event)" @cancel="popObj.showPop2 = false" />
|
||||
</van-popup>
|
||||
<div v-for="(item, index) in popObj.meetingGuestList" :key="index">
|
||||
<van-field clickable name="text" v-model="item.text" :label="`嘉宾${index + 1}`"
|
||||
:placeholder="disabledFlag ? '' : '点击选择宾'" @click="popObj.handleShowPop6('meetingGuest', '嘉宾', index)"
|
||||
type="text" autosize readonly :right-icon="disabledFlag ? '' : 'close'"
|
||||
@click-right-icon.stop="popObj.deleteItemByType('meetingGuest', index)" :disabled="disabledFlag" />
|
||||
</div>
|
||||
<van-field clickable name="newItem" v-model="newItem" label="嘉宾" placeholder="点击新增嘉宾"
|
||||
@click="popObj.handleShowPop6('meetingGuest', '嘉宾', -1)" readonly v-show="!disabledFlag" />
|
||||
<van-field clickable name="linkFromName" v-model="popObj.linkFromName" label="发起人"
|
||||
:placeholder="disabledFlag ? '' : '点击添加发起人'" @click="popObj.handleShowPop5('linkFrom', '发起人')" readonly
|
||||
:disabled="disabledFlag" />
|
||||
<van-popup v-model:show="popObj.showPop6" position="bottom">
|
||||
<div class="p-16 box-popup">
|
||||
<van-row class="mb-16">
|
||||
<van-col span="8">
|
||||
<van-button class="van-picker__cancel" native-type="button"
|
||||
@click="popObj.showPop6 = false">取消</van-button>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-center">
|
||||
<span class="sub-title f-b">请选择{{ popObj.popTitle }}</span>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-end">
|
||||
<van-button class="van-picker__confirm" native-type="button" @click="popObj.onConfirm5">确认</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<van-radio-group v-model="popObj.linkFrom" @click="popObj.changeLinkFrom">
|
||||
<van-radio :name="1">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="radioLabel">机构人员</div>
|
||||
<van-field class="flex-1 radioContent1 border-1 ms-10" v-model="popObj.linkFromField"
|
||||
placeholder="录入发起人" :disabled="popObj.linkFrom !== 1" />
|
||||
</div>
|
||||
</van-radio>
|
||||
<van-radio :name="2" class="align-items-start-radio">
|
||||
<div class="d-flex align-items-start mt-10">
|
||||
<div class="radioLabel">第一上海</div>
|
||||
<div class="flex-1 radioContent2">
|
||||
<van-radio-group class="ms-10" v-model="popObj.linkFromRadio" direction="horizontal"
|
||||
:disabled="popObj.linkFrom !== 2">
|
||||
<van-radio v-for="(item, index) in popObj.linkFromList" :key="index" :name="item.userId">
|
||||
{{ item.userName }}
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-field name="phoneBy" v-model="popObj.phoneBy" label="电话拨入方式" :placeholder="disabledFlag ? '' : '请输入电话拨入方式'"
|
||||
:disabled="disabledFlag" />
|
||||
<van-field name="netBy" v-model="popObj.netBy" label="网络端登录方式" :placeholder="disabledFlag ? '' : '请输入网络端登录方式'"
|
||||
:disabled="disabledFlag" />
|
||||
<van-field v-if="popObj.netBy" name="meetingPassword" v-model="popObj.meetingPassword" label="会议密码"
|
||||
placeholder="请输入会议密码" :disabled="disabledFlag" />
|
||||
<van-field clickable name="organizerNames" v-model="popObj.organizerNames" label="相关研究员"
|
||||
:placeholder="disabledFlag ? '' : '点击选择相关研究员'" @click="popObj.handleShowPop3('organizer', '相关研究员')" readonly
|
||||
:disabled="disabledFlag" />
|
||||
<van-popup v-model:show="popObj.showPop3" position="bottom">
|
||||
<div class="p-16 box-popup">
|
||||
<van-row class="mb-16">
|
||||
<van-col span="8">
|
||||
<van-button class="van-picker__cancel" native-type="button"
|
||||
@click="popObj.showPop3 = false">取消</van-button>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-center">
|
||||
<span class="sub-title f-b">请选择{{ popObj.popTitle }}</span>
|
||||
</van-col>
|
||||
<van-col span="8" class="text-end">
|
||||
<van-button class="van-picker__confirm" native-type="button" @click="popObj.onConfirm3">确认</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<van-checkbox-group v-model="popObj.value3" direction="horizontal">
|
||||
<van-checkbox v-for="(item, index) in popObj[`${popObj.type}List`]" :key="index" :name="item.value"
|
||||
shape="square">
|
||||
{{ item.text }}
|
||||
</van-checkbox>
|
||||
</van-checkbox-group>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-field name="content" v-model="popObj.content" label="备注" rows="3" type="textarea" autosize
|
||||
:disabled="disabledFlag" />
|
||||
<van-field clickable name="meetingByName" v-model="popObj.visibleName" label="可见范围"
|
||||
:placeholder="disabledFlag ? '' : '点击选择可见范围'" @click="popObj.handleShowPop1('visible', '可见范围')" readonly
|
||||
:disabled="disabledFlag" />
|
||||
<van-field v-if="popObj.selectvisible === '3'" clickable name="staffNames" v-model="popObj.staffNames"
|
||||
label="可见员工" :placeholder="disabledFlag ? '' : '点击选择可见员工'" @click="popObj.handleShowPop3('staff', '可见员工')"
|
||||
readonly :rules="[{ required: popObj.selectvisible === '3', message: '请选择可见员工' }]"
|
||||
:required="popObj.selectvisible === '3'" :disabled="disabledFlag" />
|
||||
<div class="m-16 d-flex justify-content-center">
|
||||
<van-button round block type="warning" size="small" native-type="button" @click="changeDisabled"
|
||||
v-if="meetingId">
|
||||
{{ disabledFlag ? '修改' : '取消' }}
|
||||
</van-button>
|
||||
<van-button v-show="!disabledFlag" round block type="primary" size="small" native-type="submit"
|
||||
:disabled="submitFlag">提交</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import emitter from '@/utils/mitt'
|
||||
import {
|
||||
sysDicListByType, sysUserGetStaffAndManagerList, sysUserGetCompanyUserList, stockStockInfoLookUp,
|
||||
cusUserLookUp, workMeetingUpdateOrSave, workMeetingInfo, workMeetingUserList
|
||||
} from '@/utils/api'
|
||||
let submitFlag = ref(false)
|
||||
let disabledFlag = ref(true)
|
||||
let newItem = ref('')
|
||||
let listIndex = ref(0)
|
||||
let meetingId: any = ref('')
|
||||
import { debounceThrottle } from '@/mixins/debounce-throttle'
|
||||
const { throttle } = debounceThrottle()
|
||||
import { type MeetingObj } from '@/utils/types'
|
||||
const popObj: MeetingObj = reactive({
|
||||
showPop1: false,
|
||||
showPop2: false,
|
||||
showPop3: false,
|
||||
showPop4: false,
|
||||
showPop5: false,
|
||||
showPop6: false,
|
||||
popTitle: '',
|
||||
type: '',
|
||||
value1: [],
|
||||
value3: [],
|
||||
meetingTitle: '',
|
||||
sysDay: '',
|
||||
selectsysDay: [],
|
||||
sysTime: '',
|
||||
selectsysTime: [],
|
||||
meetingTypeName: '',
|
||||
selectmeetingType: '',
|
||||
meetingTypeList: [],
|
||||
meetingByName: '',
|
||||
selectmeetingBy: '',
|
||||
meetingByList: [],
|
||||
stockCodeList: [],
|
||||
searchValue: '',
|
||||
showPop2List: [],
|
||||
meetingGuestList: [],
|
||||
linkFromName: '',
|
||||
linkFrom: 1,
|
||||
linkFromRadio: '',
|
||||
linkFromField: '',
|
||||
linkFromList: [],
|
||||
phoneBy: '',
|
||||
netBy: '',
|
||||
meetingPassword: '',
|
||||
organizerNames: '',
|
||||
selectorganizerIds: [],
|
||||
organizerList: [],
|
||||
content: '',
|
||||
visibleName: '公开',
|
||||
selectvisible: '1',
|
||||
visibleList: [{
|
||||
value: '1',
|
||||
text: '公开'
|
||||
}, {
|
||||
value: '3',
|
||||
text: '员工'
|
||||
}],
|
||||
staffNames: '',
|
||||
staffList: [],
|
||||
selectstaffIds: [],
|
||||
handleShowPop1: (val: string, title: string) => {
|
||||
if (!disabledFlag.value) {
|
||||
popObj.showPop1 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.value1 = [popObj[`select${val}`]]
|
||||
}
|
||||
},
|
||||
handleShowPop3: (val: string, title: string) => {
|
||||
if (!disabledFlag.value) {
|
||||
popObj.showPop3 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.value3 = popObj[`select${val}Ids`]
|
||||
}
|
||||
},
|
||||
handleShowPop4: (val: string, title: string, index: number) => {
|
||||
if (!disabledFlag.value) {
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj[`value${index}`] = popObj[`select${val}`]
|
||||
popObj[`showPop${index}`] = true
|
||||
}
|
||||
},
|
||||
handleShowPop5: (val: string, title: string) => {
|
||||
if (!disabledFlag.value) {
|
||||
if (popObj.linkFrom === 1) {
|
||||
popObj.linkFromRadio = ''
|
||||
popObj.linkFromField = popObj.linkFromName
|
||||
} else {
|
||||
const index = popObj.linkFromList.findIndex((ele: any) => {
|
||||
return ele.userName === popObj.linkFromName
|
||||
})
|
||||
if (index > -1) {
|
||||
popObj.linkFromRadio = popObj.linkFromList[index].userId
|
||||
}
|
||||
}
|
||||
popObj.showPop6 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
}
|
||||
},
|
||||
handleShowPop6: (val: string, title: string, index: number) => {
|
||||
if (!disabledFlag.value) {
|
||||
popObj.showPop2 = true
|
||||
popObj.popTitle = title
|
||||
popObj.type = val
|
||||
popObj.searchValue = index > -1 ?
|
||||
popObj[`${val}List`][index][`${val === 'stockCode' ? 'value' : 'cusUserName'}`] : ''
|
||||
popObj.showPop2List = index > -1 && popObj[`${val}List`][index].value ?
|
||||
[{ text: popObj[`${val}List`][index].text, value: popObj[`${val}List`][index].value }] : []
|
||||
listIndex.value = index
|
||||
}
|
||||
},
|
||||
onSearchInput: throttle(
|
||||
async () => {
|
||||
if (popObj.searchValue) {
|
||||
if (popObj.type === 'stockCode') {
|
||||
const { data } = await stockStockInfoLookUp({ stockCode: popObj.searchValue })
|
||||
popObj.showPop2List = data.map((ele: any) => {
|
||||
ele.text = `${ele.stockCode}【${ele.stockName}】`
|
||||
ele.value = ele.stockCode
|
||||
return ele
|
||||
})
|
||||
} else if (popObj.type === 'meetingGuest') {
|
||||
const { data } = await cusUserLookUp({ cusUserName: popObj.searchValue })
|
||||
popObj.showPop2List = data.map((ele: any) => {
|
||||
ele.text = `${ele.cusUserName}${ele.cusName ? '【' + ele.cusName + '】' : ''}`
|
||||
ele.value = ele.cusUserId
|
||||
return ele
|
||||
})
|
||||
}
|
||||
} else {
|
||||
popObj.showPop2List = []
|
||||
}
|
||||
},
|
||||
500
|
||||
),
|
||||
deleteItemByType: (type: string, index: number) => {
|
||||
if (!disabledFlag.value) {
|
||||
popObj[`${type}List`].splice(index, 1)
|
||||
}
|
||||
},
|
||||
onConfirm1: ({ selectedOptions }) => {
|
||||
if (selectedOptions) {
|
||||
popObj[`select${popObj.type}`] = selectedOptions[0].value
|
||||
popObj[`${popObj.type}Name`] = selectedOptions[0].text
|
||||
if (popObj.type === 'meetingBy') {
|
||||
if (selectedOptions[0].value === '1') {
|
||||
popObj.phoneBy = '香港:852-3005-1318\n大陆:400-810-5222\n'
|
||||
} else if (selectedOptions[0].value == 2) {
|
||||
popObj.phoneBy = '+8675536550000\n+85230018898'
|
||||
} else {
|
||||
popObj.phoneBy = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
popObj.showPop1 = false
|
||||
},
|
||||
onConfirm2: ({ selectedOptions }) => {
|
||||
if (selectedOptions && selectedOptions[0]) {
|
||||
if (listIndex.value > -1) {
|
||||
popObj[`${popObj.type}List`][listIndex.value].value = selectedOptions[0].value
|
||||
popObj.stockCodeList[listIndex.value][`${popObj.type === 'stockCode' ? 'stockName' : 'cusUserName'}`] =
|
||||
selectedOptions[0][`${popObj.type === 'stockCode' ? 'stockName' : 'cusUserName'}`]
|
||||
popObj[`${popObj.type}List`][listIndex.value].text = selectedOptions[0].text
|
||||
} else {
|
||||
let data: any = {
|
||||
value: selectedOptions[0].value,
|
||||
text: selectedOptions[0].text
|
||||
}
|
||||
if (popObj.type === 'stockCode') {
|
||||
data.stockName = selectedOptions[0].stockName
|
||||
} else if (popObj.type === 'meetingGuest') {
|
||||
data.cusUserName = selectedOptions[0].cusUserName
|
||||
}
|
||||
popObj[`${popObj.type}List`].push(data)
|
||||
}
|
||||
popObj.showPop2 = false
|
||||
} else {
|
||||
if (popObj.type === 'stockCode') {
|
||||
showToast(`请选择${popObj.popTitle}后,再确认`)
|
||||
} else {
|
||||
popObj.meetingGuestList.push({
|
||||
value: '',
|
||||
cusUserName: popObj.searchValue,
|
||||
text: popObj.searchValue
|
||||
})
|
||||
popObj.showPop2 = false
|
||||
}
|
||||
}
|
||||
},
|
||||
onConfirm3: () => {
|
||||
popObj[`select${popObj.type}Ids`] = popObj.value3
|
||||
const sameList = popObj[`${popObj.type}List`].filter((ele: any) => popObj.value3.some((res: any) => {
|
||||
return ele.value === res
|
||||
}))
|
||||
let names = ''
|
||||
sameList.forEach((ele: any) => {
|
||||
names += `${ele.text},`
|
||||
})
|
||||
if (names) {
|
||||
names = names.substring(0, names.length - 1)
|
||||
}
|
||||
popObj[`${popObj.type}Names`] = names
|
||||
popObj.showPop3 = false
|
||||
},
|
||||
onConfirm4: ({ selectedValues }) => {
|
||||
if (selectedValues) {
|
||||
popObj[popObj.type] = `${selectedValues[0]}${popObj.type === 'sysDay' ? '-' + selectedValues[1] + '-' + selectedValues[2] : ':' + selectedValues[1]}`
|
||||
popObj[`select${popObj.type}`] = selectedValues
|
||||
}
|
||||
if (popObj.type === 'sysDay') {
|
||||
popObj.showPop4 = false
|
||||
} else {
|
||||
popObj.showPop5 = false
|
||||
}
|
||||
},
|
||||
changeLinkFrom: () => {
|
||||
if (popObj.linkFrom === 1) {
|
||||
popObj.linkFromRadio = ''
|
||||
} else if (popObj.linkFrom === 2) {
|
||||
popObj.linkFromField = ''
|
||||
}
|
||||
},
|
||||
onConfirm5: () => {
|
||||
if (popObj.linkFrom === 1) {
|
||||
popObj.linkFromName = popObj.linkFromField
|
||||
popObj.linkFromRadio = ''
|
||||
popObj.showPop6 = false
|
||||
} else if (popObj.linkFrom === 2) {
|
||||
popObj.linkFromField = ''
|
||||
const index = popObj.linkFromList.findIndex((ele: any) => {
|
||||
return ele.userId === popObj.linkFromRadio
|
||||
})
|
||||
if (index > -1) {
|
||||
popObj.linkFromName = popObj.linkFromList[index].userName
|
||||
popObj.showPop6 = false
|
||||
} else {
|
||||
showToast('请选择发起人')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
import moment from 'moment'
|
||||
const getSelectList = async () => {
|
||||
emitter.emit('showLoading', '')
|
||||
const data1 = await sysDicListByType({ dicType: 'meeting_type' })
|
||||
popObj.meetingTypeList = data1.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data2 = await sysDicListByType({ dicType: 'meeting_by' })
|
||||
popObj.meetingByList = data2.data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
const data3 = await sysUserGetStaffAndManagerList()
|
||||
popObj.organizerList = data3.data.map((ele: any) => {
|
||||
ele.value = ele.userId
|
||||
ele.text = ele.userName
|
||||
return ele
|
||||
})
|
||||
const data4 = await sysUserGetCompanyUserList()
|
||||
popObj.staffList = data4.data.map((ele: any) => {
|
||||
ele.value = ele.userId
|
||||
ele.text = ele.userName
|
||||
return ele
|
||||
})
|
||||
popObj.linkFromList = data4.data
|
||||
if (route.query && route.query.addDay) {
|
||||
disabledFlag.value = false
|
||||
popObj.sysDay = route.query.addDay
|
||||
popObj.selectsysDay = popObj.sysDay.split('-')
|
||||
popObj.sysTime = '08:00'
|
||||
popObj.selectsysTime = ['08', '00']
|
||||
emitter.emit('hiddenLoading', '')
|
||||
} else if (route.query && route.query.meetingId) {
|
||||
disabledFlag.value = true
|
||||
meetingId.value = route.query.meetingId
|
||||
getMeeingInfo()
|
||||
emitter.emit('setTitle', { title: '修改会议', type: 'meetingType' })
|
||||
} else {
|
||||
disabledFlag.value = false
|
||||
let nowDay = moment().format('YYYY-MM-DD')
|
||||
popObj.sysDay = nowDay
|
||||
popObj.selectsysDay = nowDay.split('-')
|
||||
popObj.sysTime = '08:00'
|
||||
popObj.selectsysTime = ['08', '00']
|
||||
emitter.emit('setTitle', { title: '新增会议', type: 'meetingType' })
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
}
|
||||
|
||||
const getMeeingInfo = async () => {
|
||||
const { data } = await workMeetingInfo(meetingId.value)
|
||||
popObj.meetingTitle = data.meetingTitle
|
||||
popObj.sysDay = data.meetingTime.slice(0, 10)
|
||||
popObj.selectsysDay = data.meetingTime.split('-')
|
||||
popObj.sysTime = data.meetingTime.slice(11, 16)
|
||||
popObj.selectsysTime = data.meetingTime.split(':')
|
||||
popObj.meetingTypeName = data.meetingTypeName
|
||||
popObj.selectmeetingType = data.meetingType + ''
|
||||
popObj.meetingByName = data.meetingByName
|
||||
popObj.selectmeetingBy = data.meetingBy + ''
|
||||
let codeList = data.stockCodes ? data.stockCodes.split(',') : []
|
||||
let nameList = data.stockNames ? data.stockNames.split(',') : []
|
||||
popObj.stockCodeList = codeList.map((ele: any, index: number) => {
|
||||
return {
|
||||
value: ele,
|
||||
text: `${ele}${nameList.length > index ? '【' + nameList[index] + '】' : ''}`,
|
||||
stockName: nameList.length > index ? nameList[index] : '',
|
||||
}
|
||||
})
|
||||
let guestNameList = data.meetingGuestNames ? data.meetingGuestNames.split(',') : []
|
||||
let guestIdList = data.meetingGuestIds ? data.meetingGuestIds.split(',') : []
|
||||
popObj.meetingGuestList = guestNameList.map((ele: any, index: number) => {
|
||||
return {
|
||||
value: guestIdList.length > index ? guestIdList[index] : '',
|
||||
text: ele,
|
||||
cusUserName: ele,
|
||||
}
|
||||
})
|
||||
popObj.linkFrom = data.oranizerId && data.oranizerId !== -1 ? 2 : 1
|
||||
popObj.linkFromName = data.oranizerName
|
||||
popObj.phoneBy = data.phoneBy
|
||||
popObj.netBy = data.netBy
|
||||
popObj.meetingPassword = data.meetingPassword
|
||||
popObj.organizerNames = data.oranizerName
|
||||
popObj.selectorganizerIds = data.oranizerId ? data.oranizerId.split(',').map(Number) : []
|
||||
popObj.content = data.content
|
||||
popObj.visibleName = data.visible === 1 ? '公开' : '员工'
|
||||
popObj.selectvisible = data.visible + ''
|
||||
if (data.visible === 3) {
|
||||
getVisible()
|
||||
}
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
|
||||
const getVisible = async () => {
|
||||
const { data } = await workMeetingUserList({ meetingId: meetingId.value })
|
||||
let staffNames = ''
|
||||
let selectstaffIds: any = []
|
||||
data.forEach((ele: any, index: number) => {
|
||||
staffNames += `${ele.userName}${index < data.length - 1 ? ',' : ''}`
|
||||
selectstaffIds.push(ele.userId)
|
||||
})
|
||||
popObj.staffNames = staffNames
|
||||
popObj.selectstaffIds = selectstaffIds
|
||||
}
|
||||
|
||||
const handleKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keyup', handleKeyUp)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keyup', handleKeyUp)
|
||||
})
|
||||
|
||||
import listRefreshInfoStore from '@/stores/listRefreshInfo'
|
||||
const listRefreshInfo = listRefreshInfoStore()
|
||||
const onSubmit = async () => {
|
||||
submitFlag.value = true
|
||||
let stockCodeList: any = []
|
||||
popObj.stockCodeList.forEach((ele: any) => {
|
||||
if (ele.value) {
|
||||
stockCodeList.push({
|
||||
stockCode: ele.value,
|
||||
stockName: ele.stockName
|
||||
})
|
||||
}
|
||||
})
|
||||
let meetingGuestList: any = []
|
||||
popObj.meetingGuestList.forEach((ele: any) => {
|
||||
if (ele.cusUserName) {
|
||||
meetingGuestList.push({
|
||||
cusUserId: ele.value || -1,
|
||||
cusUserName: ele.cusUserName
|
||||
})
|
||||
}
|
||||
})
|
||||
const res: any = {
|
||||
meetingTitle: popObj.meetingTitle,
|
||||
meetingTime: `${popObj.sysDay} ${popObj.sysTime}:00`,
|
||||
meetingType: popObj.selectmeetingType,
|
||||
meetingBy: popObj.selectmeetingBy,
|
||||
stockCodeList: stockCodeList || [],
|
||||
meetingGuestList: meetingGuestList || [],
|
||||
linkFrom: popObj.linkFromName || null,
|
||||
linkFromId: popObj.linkFrom === 2 && popObj.linkFromRadio ? popObj.linkFromRadio : null,
|
||||
phoneBy: popObj.phoneBy || null,
|
||||
netBy: popObj.netBy || null,
|
||||
meetingPassword: popObj.netBy && popObj.meetingPassword ? popObj.meetingPassword : null,
|
||||
organizerIdList: popObj.selectorganizerIds || [],
|
||||
content: popObj.content || null,
|
||||
visible: popObj.selectvisible,
|
||||
userIdList: popObj.selectvisible === '3' ? popObj.selectstaffIds : [],
|
||||
meetingId: meetingId.value || null
|
||||
}
|
||||
const data: any = await workMeetingUpdateOrSave(meetingId.value, res)
|
||||
if (data.code === 0) {
|
||||
showToast('提交成功')
|
||||
if (meetingId.value) {
|
||||
listRefreshInfo.refreshId = meetingId.value
|
||||
} else {
|
||||
listRefreshInfo.addId = '1'
|
||||
}
|
||||
router.go(-1)
|
||||
} else {
|
||||
submitFlag.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const changeDisabled = () => {
|
||||
disabledFlag.value = !disabledFlag.value
|
||||
if (disabledFlag.value) {
|
||||
getMeeingInfo()
|
||||
}
|
||||
}
|
||||
|
||||
getSelectList()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.add-meeting {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detailClass1 {
|
||||
.van-cell {
|
||||
margin: 12px 16px 0;
|
||||
width: calc(100vw - 32px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
width: 646px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detailClass2 {
|
||||
.van-cell {
|
||||
margin: 0 16px;
|
||||
width: calc(100vw - 32px);
|
||||
box-shadow: 0 0;
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
width: 646px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-field--disabled) .van-field__label {
|
||||
color: #646566;
|
||||
}
|
||||
|
||||
:deep(.van-field__control:disabled) {
|
||||
-webkit-text-fill-color: #000 !important;
|
||||
}
|
||||
|
||||
:deep(.van-field__control) {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
:deep(.van-checkbox) {
|
||||
margin-right: 0 !important;
|
||||
width: 50%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
:deep(.van-radio) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.radioLabel {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.radioContent1 {
|
||||
max-width: 220px;
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
|
||||
.radioContent2 {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
|
||||
.van-radio {
|
||||
margin-right: 0 !important;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 50%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.align-items-start-radio) {
|
||||
align-items: flex-start !important;
|
||||
max-height: 300px;
|
||||
|
||||
.van-radio__icon {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-radio-group {
|
||||
.van-cell {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ms-10) {
|
||||
.van-radio__icon {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
436
src/views/meeting/detail.vue
Normal file
@@ -0,0 +1,436 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="meeting-detail">
|
||||
<div class="normal-title text-center p-10 f-b">
|
||||
{{ meetingData.meetingTitle }}
|
||||
<van-icon v-if="isAuth('calendar:update') || userInfo.id === meetingData.createUser" name="edit"
|
||||
class="warning-text-color"
|
||||
@click="$router.push({ name: 'add-or-update-meeting', query: { meetingId: meetingId } })" />
|
||||
<van-icon v-if="isAuth('calendar:update') || userInfo.id === meetingData.createUser" name="delete-o"
|
||||
class="danger-text-color" @click="deleteMeeting" />
|
||||
</div>
|
||||
<div class="text-center m-5 f-b">{{ meetingData.meetingTime }}</div>
|
||||
<div class=" text-center m-5">
|
||||
<van-tag type="success" size="medium">{{ meetingData.meetingTypeName }}</van-tag>
|
||||
</div>
|
||||
<van-grid class="baseData" :column-num="2">
|
||||
<van-grid-item>会议形式</van-grid-item>
|
||||
<van-grid-item>{{ meetingData.meetingByName }}</van-grid-item>
|
||||
<van-grid-item>股票名称</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.stockNames }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>股票代码</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.stockCodes }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>电话拨入方式</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.phoneBy }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>网络端登录方式</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.netBy }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>会议密码</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.meetingPassword }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>嘉宾</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.meetingGuestList }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>研究员</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.oranizerName }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>发起人</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.organizer }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>IS参会人数</van-grid-item>
|
||||
<van-grid-item>{{ meetingData.meetingCusUserNum }}</van-grid-item>
|
||||
<van-grid-item>IS提问人数</van-grid-item>
|
||||
<van-grid-item>{{ meetingData.questionNum }}</van-grid-item>
|
||||
<van-grid-item>总参会人数</van-grid-item>
|
||||
<van-grid-item>{{ meetingData.userNumHigh }}</van-grid-item>
|
||||
<van-grid-item>总提问人数</van-grid-item>
|
||||
<van-grid-item>{{ meetingData.queationNumAll }}</van-grid-item>
|
||||
<van-grid-item>可见范围</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.visible === 1 ? '公开' : meetingData.visibleName }}</div>
|
||||
</van-grid-item>
|
||||
<van-grid-item>备注</van-grid-item>
|
||||
<van-grid-item>
|
||||
<div class="grid-item-text">{{ meetingData.content }}</div>
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
<div class="sub-title m-16">会议资料</div>
|
||||
<file-list ref="meetingFile" />
|
||||
<div class="sub-title m-16">参会资料</div>
|
||||
<file-list ref="joinMeetingFile" />
|
||||
<div class="sub-title m-16 d-flex align-items-center">
|
||||
参会人员
|
||||
<van-button type="primary" size="mini" @click="addJoinPerson(1)" style="margin-left: 4px">报名</van-button>
|
||||
<van-button type="primary" size="mini" @click="addJoinPerson(2)">签到</van-button>
|
||||
</div>
|
||||
<van-popup v-model:show="showPersonPopup" position="bottom">
|
||||
<div class="p-10 box-popup">
|
||||
<div class="personTitle">当前添加联系人:{{ addPerson }}</div>
|
||||
<van-field v-model="searchCuseUser" placeholder="查找联系人" @input="onSearchInput"
|
||||
class="border-1 border-radius-8" />
|
||||
<div class="tip">
|
||||
<van-row v-if="personType === 2 || personType === 3">
|
||||
<van-col span="12">
|
||||
<van-field name="switch" label="签到">
|
||||
<template #input>
|
||||
<van-switch v-model="attend" size="20" :disabled="personType === 2" />
|
||||
</template>
|
||||
</van-field>
|
||||
</van-col>
|
||||
<van-col span="12">
|
||||
<van-field name="switch" label="提问" v-show="attend">
|
||||
<template #input>
|
||||
<van-switch v-model="question" size="20" />
|
||||
</template>
|
||||
</van-field>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<van-picker show-toolbar title="" :columns="cusUserList" @click-option="onCusUserConfirm"
|
||||
@cancel="showPersonPopup = false">
|
||||
<template #confirm>
|
||||
<span></span>
|
||||
</template>
|
||||
</van-picker>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||
<van-swipe-cell v-for="(item, index) in joinPersonList" :key="item.cusUserId">
|
||||
<van-cell>
|
||||
<div class="sub-title text-start balck-text-color">{{ item.cusUserName }}</div>
|
||||
<div class="d-flex">
|
||||
<div class="flex-1">
|
||||
<div class="content text-start">{{ item.cusName }}</div>
|
||||
<div class="content text-start">{{ item.positionName }}</div>
|
||||
</div>
|
||||
<div class="flex-1 text-end">
|
||||
<div class="content" :class="item.attend ? 'primary-text-color' : ''">
|
||||
{{ item.attend ? '已到场' : '已报名' }}
|
||||
</div>
|
||||
<div class="content" v-if="item.attend" :class="item.question ? 'danger-text-color' : ''">
|
||||
{{ item.attend ? item.question ? '已提问' : '未提问' : '' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
<template #right>
|
||||
<van-button square type="danger" text="删除" @click="deleteJoinPerson(item, index)" />
|
||||
<van-button square type="primary" text="修改" @click="changeJoinPerson(item)" />
|
||||
</template>
|
||||
</van-swipe-cell>
|
||||
</van-list>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import FileList from '@/components/file-list.vue'
|
||||
import emitter from '@/utils/mitt'
|
||||
import { isAuth } from '@/utils/index'
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
const userInfo = userInfoStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
let meetingData = ref({
|
||||
meetingTime: '',
|
||||
meetingTitle: '',
|
||||
meetingTypeName: '',
|
||||
meetingByName: '',
|
||||
stockCodes: '',
|
||||
stockNames: '',
|
||||
phoneBy: '',
|
||||
netBy: '',
|
||||
meetingPassword: '',
|
||||
meetingCusUserNum: '',
|
||||
questionNum: '',
|
||||
userNumHigh: '',
|
||||
queationNumAll: '',
|
||||
meetingGuestList: '',
|
||||
organizer: '',
|
||||
oranizerName: '',
|
||||
visible: undefined,
|
||||
visibleName: '',
|
||||
content: '',
|
||||
createUser: ''
|
||||
})
|
||||
let meetingId: any = ref('')
|
||||
const meetingFile: any = ref(null)
|
||||
const joinMeetingFile: any = ref(null)
|
||||
import fileInfoStore from '@/stores/fileInfo'
|
||||
const fileInfo = fileInfoStore()
|
||||
|
||||
const init = () => {
|
||||
if (route.query && route.query.meetingId) {
|
||||
meetingId.value = route.query.meetingId
|
||||
emitter.emit('showLoading', '')
|
||||
getMeetingDetail()
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
|
||||
import {
|
||||
workMeetingInfo, workMeetingUserList, workMeetingGetFileId, workMeetinGetUserFileMenuId,
|
||||
cusUserMeetingLogList, cusUserMeetingLogDelete, cusUserLookUp, cusUserMeetingLogUpdateOrSave,
|
||||
workMeetingDelete
|
||||
} from '@/utils/api'
|
||||
const getMeetingDetail = async () => {
|
||||
const data1: any = await workMeetingInfo(meetingId.value)
|
||||
let meetingGuestNameList = data1.data.meetingGuestNames
|
||||
? data1.data.meetingGuestNames.split(',')
|
||||
: []
|
||||
let meetingGuestList = ''
|
||||
meetingGuestNameList.forEach((item: any, index: number) => {
|
||||
meetingGuestList +=
|
||||
`
|
||||
${item}
|
||||
${index < meetingGuestNameList.length - 1 ? ',' : ''}
|
||||
`
|
||||
})
|
||||
data1.data.meetingGuestList = meetingGuestList
|
||||
meetingData.value = data1.data
|
||||
emitter.emit('setTitle', { title: meetingData.value.meetingTitle, type: 'meeting' })
|
||||
if (data1.data.visible === 3) {
|
||||
getVisible()
|
||||
}
|
||||
const data2: any = await workMeetingGetFileId({ meetingId: meetingId.value })
|
||||
if (meetingFile.value) {
|
||||
meetingFile.value.init(data2.data)
|
||||
}
|
||||
const data3: any = await workMeetinGetUserFileMenuId({ meetingId: meetingId.value })
|
||||
if (joinMeetingFile.value) {
|
||||
joinMeetingFile.value.init(data3.data)
|
||||
}
|
||||
nextTick(() => {
|
||||
if (fileInfo.fileId) {
|
||||
const el = document.getElementById(fileInfo.fileId)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
fileInfo.fileId = ''
|
||||
emitter.emit('hiddenLoading', '')
|
||||
} else {
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getVisible = async () => {
|
||||
const { data } = await workMeetingUserList({ meetingId: meetingId.value })
|
||||
let name = ''
|
||||
data.forEach((ele: any, index: number) => {
|
||||
name += `${ele.userName}${index < data.length - 1 ? ',' : ''}`
|
||||
})
|
||||
meetingData.value.visibleName = name
|
||||
}
|
||||
let finished = ref(false)
|
||||
let loading = ref(false)
|
||||
let curPage = ref(1)
|
||||
let joinPersonList = ref<{ cusUserId: string, cusUserName: string, cusName: string, positionName: string, attend: boolean, question: boolean }[]>([])
|
||||
const onLoad = async () => {
|
||||
loading.value = true
|
||||
const { data } = await cusUserMeetingLogList({
|
||||
curPage: curPage.value,
|
||||
limit: 20,
|
||||
meetingId: meetingId.value
|
||||
})
|
||||
|
||||
data.list.map((ele: any) => {
|
||||
ele.question = !!ele.question
|
||||
ele.attend = !!ele.attend
|
||||
})
|
||||
joinPersonList.value = joinPersonList.value.concat(data.list)
|
||||
if (joinPersonList.value.length >= data.totalCount) {
|
||||
finished.value = true
|
||||
} else {
|
||||
curPage.value++
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
let attend = ref(false)
|
||||
let question = ref(false)
|
||||
let searchCuseUser = ref('')
|
||||
let meetingUserId = ref('')
|
||||
let personType = ref()
|
||||
let showPersonPopup = ref(false)
|
||||
let addPerson = ref('')
|
||||
let cusUserList = ref<{ text: string, value: string, cusUserName: string, cusName: string, positionName: string, cusUserId: string }[]>([])
|
||||
const addJoinPerson = (index: number) => {
|
||||
personType.value = index
|
||||
searchCuseUser.value = ''
|
||||
attend.value = false
|
||||
if (index === 2) {
|
||||
attend.value = true
|
||||
}
|
||||
question.value = false
|
||||
cusUserList.value = []
|
||||
meetingUserId.value = ''
|
||||
showPersonPopup.value = true
|
||||
}
|
||||
|
||||
const changeJoinPerson = (e: any) => {
|
||||
personType.value = 3
|
||||
searchCuseUser.value = e.cusUserName
|
||||
attend.value = e.attend
|
||||
question.value = e.question
|
||||
cusUserList.value = [{
|
||||
cusUserName: e.cusUserName,
|
||||
cusName: e.cusName,
|
||||
positionName: e.positionName,
|
||||
cusUserId: e.cusUserId,
|
||||
text: `${e.cusUserName}${e.cusName ? '【' + e.cusName + '】' : ''}${e.positionName ? '(' + e.positionName + ')' : ''}`,
|
||||
value: e.cusUserId
|
||||
}]
|
||||
meetingUserId.value = e.meetingUserId
|
||||
showPersonPopup.value = true
|
||||
}
|
||||
|
||||
import { debounceThrottle } from '@/mixins/debounce-throttle'
|
||||
const { throttle } = debounceThrottle()
|
||||
const onSearchInput = throttle(
|
||||
async () => {
|
||||
if (searchCuseUser.value) {
|
||||
const { data } = await cusUserLookUp({ cusUserName: searchCuseUser.value })
|
||||
cusUserList.value = data.map((ele: any) => {
|
||||
ele.text = `${ele.cusUserName}${ele.cusName ? '【' + ele.cusName + '】' : ''}${ele.positionName ? '(' + ele.positionName + ')' : ''}`
|
||||
ele.value = ele.cusUserId
|
||||
return ele
|
||||
})
|
||||
if (data.length === 1) {
|
||||
const res = {
|
||||
cusUserId: data[0].cusUserId,
|
||||
attend: attend.value ? 1 : 0,
|
||||
question: question.value ? 1 : 0,
|
||||
meetingId: meetingId.value,
|
||||
meetingUserId: meetingUserId.value || null
|
||||
}
|
||||
await cusUserMeetingLogUpdateOrSave(meetingUserId.value, res)
|
||||
addPerson.value = `${data[0].cusUserName},${addPerson.value}`
|
||||
searchCuseUser.value = ''
|
||||
cusUserList.value = []
|
||||
}
|
||||
} else {
|
||||
cusUserList.value = []
|
||||
}
|
||||
},
|
||||
500
|
||||
)
|
||||
|
||||
const onCusUserConfirm = async (e: any) => {
|
||||
if (e.selectedIndexes[0] > -1) {
|
||||
const res = {
|
||||
cusUserId: e.selectedOptions[0].value,
|
||||
attend: attend.value ? 1 : 0,
|
||||
question: question.value ? 1 : 0,
|
||||
meetingId: meetingId.value,
|
||||
meetingUserId: meetingUserId.value || null
|
||||
}
|
||||
await cusUserMeetingLogUpdateOrSave(meetingUserId.value, res)
|
||||
addPerson.value = `${e.selectedOptions[0].cusUserName},${addPerson.value}`
|
||||
searchCuseUser.value = ''
|
||||
cusUserList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => showPersonPopup.value, (val) => {
|
||||
if (!val) {
|
||||
curPage.value = 1
|
||||
joinPersonList.value = []
|
||||
finished.value = false
|
||||
addPerson.value = ''
|
||||
onLoad()
|
||||
}
|
||||
})
|
||||
|
||||
const deleteJoinPerson = (e: any, index: number) => {
|
||||
showConfirmDialog({
|
||||
title: '提示',
|
||||
message:
|
||||
`是否确认删除${e.cusUserName}参加${meetingData.value.meetingTime} ${meetingData.value.meetingTitle}记录`,
|
||||
}).then(async () => {
|
||||
const data: any = await cusUserMeetingLogDelete([e.meetingUserId])
|
||||
if (data.code === 0) {
|
||||
showToast('删除成功')
|
||||
joinPersonList.value.splice(index, 1)
|
||||
}
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
init()
|
||||
|
||||
import listRefreshInfoStore from '@/stores/listRefreshInfo'
|
||||
const listRefreshInfo = listRefreshInfoStore()
|
||||
const deleteMeeting = () => {
|
||||
showConfirmDialog({
|
||||
title: '提示',
|
||||
message:
|
||||
`是否确认删除会议${meetingData.value.meetingTitle}`,
|
||||
}).then(async () => {
|
||||
const data: any = await workMeetingDelete(meetingId.value)
|
||||
if (data.code === 0) {
|
||||
showToast('删除成功')
|
||||
listRefreshInfo.deleteId = meetingId.value
|
||||
router.go(-1)
|
||||
}
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.meeting-detail {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.baseData {
|
||||
:deep(.van-grid-item:nth-child(2n)) {
|
||||
.van-grid-item__content {
|
||||
color: #1989fa;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-grid-item__content) {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
padding: 8px 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-item-text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
max-width: calc(50vw - 16px);
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
max-width: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-swipe-cell__right) {
|
||||
.van-button {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.personTitle {
|
||||
height: 60px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
237
src/views/meeting/list.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<div class="page-content tabbar-list top-select-list" ref="pageContent">
|
||||
<div class="addDiv success-background-color" v-if="isAuth('calendar:update')"
|
||||
@click=" $router.push({ name: 'add-or-update-meeting' , query: { addDay: selectDay } })">
|
||||
<div class="text-center addDiv1">新增</div>
|
||||
<div class="text-center addDiv2">会议</div>
|
||||
</div>
|
||||
<div class="top-select border-bottom-1">
|
||||
<van-dropdown-menu>
|
||||
<van-dropdown-item :title="searchContent" ref="itemRef">
|
||||
<van-field v-model="search" placeholder="搜索" :right-icon="search ? 'clear' : ''"
|
||||
@click-right-icon.stop="clearSearch" />
|
||||
<div style="padding: 5px 16px;">
|
||||
<van-button type="primary" size="small" block round @click="onConfirm">
|
||||
确认
|
||||
</van-button>
|
||||
</div>
|
||||
</van-dropdown-item>
|
||||
<van-dropdown-item v-model="meetingType" :options="meetingTypeList" @change="getMeetingList" />
|
||||
<van-dropdown-item v-model="createUser" :options="createUserList" @change="getMeetingList" />
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
<van-calendar title="" :poppable="false" :show-confirm="false" switch-mode="year-month" first-day-of-week="1"
|
||||
:style="{ height: '410px' }" :formatter="formatter" :default-date="defaultDate" @panel-change="panelChange"
|
||||
@select="onDateConfirm">
|
||||
</van-calendar>
|
||||
<div v-if="list.length > 0">
|
||||
<van-cell v-for="item in list" :key="item.meetingId"
|
||||
@click="$router.push({ name: 'meeting-detail', query: { meetingId: item.meetingId } })">
|
||||
<div class="text-start activeColor sub-title">{{ item.meetingHour }}</div>
|
||||
<div class="normal-title text-start balck-text-color mb-5 mt-5">{{ item.meetingTitle }}</div>
|
||||
<div class="text-end">
|
||||
<van-tag type="primary" size="medium">{{ item.meetingTypeName }}</van-tag>
|
||||
<van-tag type="danger" class="ms-10" size="medium">{{ item.meetingByName }}</van-tag>
|
||||
</div>
|
||||
</van-cell>
|
||||
</div>
|
||||
<van-empty v-else description="暂无会议" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { sysDicListByType, workMeetingList, workMeetingInfo, workStaffLookup } from '@/utils/api'
|
||||
import { convertToUrl } from '@/utils'
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
import { isAuth } from '@/utils/index'
|
||||
|
||||
let search = ref('')
|
||||
let searchContent = ref('搜索')
|
||||
let itemRef = ref()
|
||||
const clearSearch = () => {
|
||||
search.value = ''
|
||||
searchContent.value = '搜索'
|
||||
}
|
||||
const onConfirm = () => {
|
||||
searchContent.value = search.value || '搜索'
|
||||
itemRef.value.toggle()
|
||||
getMeetingList()
|
||||
}
|
||||
|
||||
let meetingType = ref('1,2,3,16,17,4,5,15,6,10,11,12,13,14,7')
|
||||
let meetingTypeList: any = ref([])
|
||||
const getMeetingTypeList = async () => {
|
||||
const { data } = await sysDicListByType({ dicType: 'meeting_type' })
|
||||
meetingTypeList.value = data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
meetingTypeList.value.unshift({ text: '全部类型', value: '1,2,3,16,17,4,5,15,6,10,11,12,13,14,7' })
|
||||
}
|
||||
getMeetingTypeList()
|
||||
|
||||
let createUser = ref('')
|
||||
let createUserList: any = ref([])
|
||||
const getCreateUserList = async () => {
|
||||
const { data } = await workStaffLookup()
|
||||
createUserList.value = data.map((ele: any) => {
|
||||
ele.text = ele.staffName
|
||||
ele.value = ele.staffId
|
||||
return ele
|
||||
})
|
||||
createUserList.value.unshift({ text: '全部创建人', value: '' })
|
||||
}
|
||||
getCreateUserList()
|
||||
|
||||
let defaultDate = ref()
|
||||
import moment from 'moment'
|
||||
const panelChange = (e: any) => {
|
||||
dateBegin.value = moment(e.date).startOf('month').format('YYYY-MM-DD')
|
||||
dateEnd.value = moment(e.date).endOf('month').format('YYYY-MM-DD')
|
||||
getMeetingList()
|
||||
}
|
||||
|
||||
const onDateConfirm = (e: any) => {
|
||||
defaultDate.value = e
|
||||
getList()
|
||||
}
|
||||
|
||||
let list: any = ref([])
|
||||
let selectDay = ref('')
|
||||
const getList = () => {
|
||||
list.value = []
|
||||
selectDay.value = moment(defaultDate.value).format('YYYY-MM-DD')
|
||||
meetingList.value.forEach((ele: any) => {
|
||||
if (ele.meetingDate === selectDay.value) {
|
||||
list.value = ele.list
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formatter = computed(() => {
|
||||
if (meetingList.value.length === 0) {
|
||||
return (day: any) => day
|
||||
}
|
||||
return (day: any) => {
|
||||
let time = moment(day.date).format('YYYY-MM-DD')
|
||||
meetingList.value.forEach((ele: any) => {
|
||||
if (ele.meetingDate === time) {
|
||||
day.topInfo = ele.num
|
||||
}
|
||||
})
|
||||
return day
|
||||
}
|
||||
})
|
||||
|
||||
const dateBegin = ref()
|
||||
const dateEnd = ref()
|
||||
const meetingList: any = ref([])
|
||||
const getMeetingList = async () => {
|
||||
let url = getMeetingParams()
|
||||
const { data } = await workMeetingList(url)
|
||||
let map = new Map()
|
||||
data.forEach((item: any, index: any, arr: any) => {
|
||||
if (!map.has(item.meetingDate)) {
|
||||
map.set(
|
||||
item.meetingDate,
|
||||
arr.filter((a: any) => a.meetingDate == item.meetingDate)
|
||||
)
|
||||
}
|
||||
})
|
||||
let res = Array.from(map).map((item) => [...item[1]])
|
||||
meetingList.value = []
|
||||
res.forEach((ele: any) => {
|
||||
meetingList.value.push({ meetingDate: ele[0].meetingDate, num: ele.length, list: ele })
|
||||
})
|
||||
getList()
|
||||
}
|
||||
|
||||
const getMeetingParams = () => {
|
||||
const res = {
|
||||
dateBegin: dateBegin.value,
|
||||
dateEnd: dateEnd.value,
|
||||
createUser: createUser.value,
|
||||
cusName: search.value,
|
||||
meetingType: meetingType.value
|
||||
}
|
||||
const url = convertToUrl(res)
|
||||
return url
|
||||
}
|
||||
|
||||
let pageContent = ref(null)
|
||||
const scrollPosition = ref(0)
|
||||
const refresh = async (type: any, id: any) => {
|
||||
if (type === 'delete' || type === 'refresh') {
|
||||
let dayIndex = meetingList.value.findIndex((ele: any) => {
|
||||
return ele.meetingDate === selectDay.value
|
||||
})
|
||||
if (dayIndex > -1) {
|
||||
let index = meetingList.value[dayIndex].list.findIndex((ele: any) => {
|
||||
return Number(ele.meetingId) === Number(id)
|
||||
})
|
||||
if (type === 'delete') {
|
||||
if (index > -1) {
|
||||
meetingList.value[dayIndex].list.splice(index, 1)
|
||||
meetingList.value[dayIndex].num -= 1
|
||||
}
|
||||
} else {
|
||||
if (index > -1) {
|
||||
const { data } = await workMeetingInfo(id)
|
||||
meetingList.value[dayIndex].list[index] = data
|
||||
}
|
||||
getList()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defaultDate.value = new Date()
|
||||
dateBegin.value = moment().startOf('month').format('YYYY-MM-DD')
|
||||
dateEnd.value = moment().endOf('month').format('YYYY-MM-DD')
|
||||
getMeetingList()
|
||||
}
|
||||
}
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, refresh)
|
||||
setScrollTop()
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'meeting-detail')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
:deep(.van-calendar__header-title) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.van-calendar__day) {
|
||||
border: 1px solid #f2f2f2;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.van-calendar__top-info) {
|
||||
width: 14px;
|
||||
left: calc(100vw / 7 - 18px);
|
||||
border-radius: 100%;
|
||||
background-color: #f56c6c;
|
||||
color: #fff;
|
||||
|
||||
@media screen and (min-width: 678px) {
|
||||
left: 74px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-calendar__selected-day) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.meetingList {
|
||||
overflow-y: auto;
|
||||
height: calc(100vh - 160px);
|
||||
}
|
||||
</style>
|
||||
487
src/views/organization/detail.vue
Normal file
@@ -0,0 +1,487 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<van-tabs v-model:active="active" sticky @change="changeTab">
|
||||
<van-tab v-for="item in tabs" :key="item.title" :title="item.title">
|
||||
<div class="p-10" v-if="active === 0">
|
||||
<div class="base-info">
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">机构名称</span>
|
||||
<span class="base-desc">{{ cusInfo.cusName }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">机构类型</span>
|
||||
<span class="base-desc">{{ cusInfo.companyTypeName }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">核心联系人</span>
|
||||
<span class="base-desc">{{ cusInfo.cusUserName }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">跟进人</span>
|
||||
<span class="base-desc">{{ cusInfo.saleUserName }}</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">城市</span>
|
||||
<span class="base-desc">{{ cusInfo.cityName }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">城市</span>
|
||||
<span class="base-desc">{{ cusInfo.cityName }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">是否开户</span>
|
||||
<span class="base-desc">{{ cusInfo.accountStatusFlag ? '是' : '否' }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">开户时间</span>
|
||||
<span class="base-desc">{{ cusInfo.accountStatusFlag && cusInfo.accountTime ? cusInfo.accountTime : ''
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">开户类型</span>
|
||||
<span class="base-desc">{{ cusInfo.accountStatusFlag && cusInfo.accountTypeName ? cusInfo.accountTypeName
|
||||
: '' }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">是否交易</span>
|
||||
<span class="base-desc">{{ cusInfo.accountStatusFlag && cusInfo.isMoneyFlag ? '是' : '否' }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">交易总金额</span>
|
||||
<span class="base-desc">{{ cusInfo.isMoneyFlag && cusInfo.accountStatusFlag && cusInfo.tradeMoney ?
|
||||
cusInfo.tradeMoney : '' }}</span>
|
||||
</div>
|
||||
<div class="d-flex base-item">
|
||||
<span class="base-title">资金规模</span>
|
||||
<span class="base-desc">{{ cusInfo.capitalScaleValue ? cusInfo.capitalScaleValue :
|
||||
cusInfo.capitalScaleName ? cusInfo.capitalScaleName : '' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title">参会趋势</div>
|
||||
<div class="chart d-flex align-items-center justify-content-center">
|
||||
<van-loading v-show="chartLoading" type="spinner" color="#1989fa" class="loading" />
|
||||
<div id="meeting_chart" class="chartDiv"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="active === 1">
|
||||
<van-list v-if="cusUserList.length" finished-text="没有更多了" :immediate-check="false">
|
||||
<van-cell v-for="item in cusUserList" :key="item.cusId">
|
||||
<div class="name1 text-start stockNames balck-text-color">{{ item.cusUserName }}<span
|
||||
class="mx-10">|</span>{{
|
||||
item.positionName }}</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="font-12">跟进人:{{ item.saleNames }}
|
||||
<span v-if="item.entryTime">({{ item.entryTime }}入职)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex font-12">
|
||||
<div>更新时间:</div>
|
||||
<div class="activeColor">{{ item.updateTime }}</div>
|
||||
</div>
|
||||
<div class="d-flex font-12 justify-content-between flex-wrap">
|
||||
<div class=" d-flex w-half">
|
||||
<div>参会次数:</div>
|
||||
<div class="activeColor">{{ item.meetingNum || 0 }}</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end w-half">
|
||||
<div>沟通次数:</div>
|
||||
<div class="activeColor">{{ item.linkNum || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
<div v-if="active === 2">
|
||||
<van-cell v-if="workList?.length" v-for="(item, index) in workList" :key="index"
|
||||
@click="showDetail(item.linkNote)">
|
||||
<div class="f-b activeColor text-start mb-5 mt-5">{{ item.saleNames }} - {{ item.linkTypeMenuName }}
|
||||
</div>
|
||||
<div class="balck-text-color text-ellipsis">{{ item.linkNote }}</div>
|
||||
<div class=" d-flex font-12 flex-wrap">
|
||||
<div>相关研究人:{{ item.staffName }};</div>
|
||||
<div>联系人:{{ item.cusUserName }}</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="activeColor font-12 text-start flex-1">{{ item.sysDate }}</div>
|
||||
<van-tag type="danger" class="ms-10" size="medium">{{ item.linkTypeName }}</van-tag>
|
||||
</div>
|
||||
</van-cell>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
<van-overlay :show="showOverLay" @click="showOverLay = false">
|
||||
<div class="wrapper">
|
||||
<div class="block" @click.stop>{{ note }}</div>
|
||||
</div>
|
||||
</van-overlay>
|
||||
<div v-if="active === 3">
|
||||
<van-cell v-if="meetingList?.length" v-for="item in meetingList" :key="item.meetingId"
|
||||
@click="$router.push({ name: 'meeting-detail', query: { meetingId: item.meetingId } })">
|
||||
<div class="f-b text-start balck-text-color mb-5 mt-5">{{ item.meetingTitle }}</div>
|
||||
<div class="d-flex">
|
||||
<div class="font-12 text-start activeColor flex-1">{{ item.meetingTime }}</div>
|
||||
<van-tag type="primary" size="medium">{{ item.meetingTypeName }}</van-tag>
|
||||
<van-tag type="danger" class="ms-10" size="medium">{{ item.meetingByName }}</van-tag>
|
||||
</div>
|
||||
</van-cell>
|
||||
<van-empty v-else description="暂无会议" />
|
||||
</div>
|
||||
<div v-if="active === 4">
|
||||
<communicate-item v-if="communicateList" class="pb-16" :list="communicateList" />
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
<div v-if="active === 5">
|
||||
<van-cell v-if="serviceList?.length" v-for="item in serviceList" :key="item.id"
|
||||
@click="showDetail(item.content)">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="f-b activeColor text-ellipsis">{{ item.content }}</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="text-start font-12">开始时间:<span class="activeColor">{{ item.serviceBeginDate }}</span></div>
|
||||
<div class="text-start font-12">状态:<span class="activeColor">{{ item.serviceStatus }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
<van-empty v-else description="暂无数据" />
|
||||
</div>
|
||||
<div v-if="active === 6">
|
||||
<van-cell v-if="yearList?.length" v-for="(item, index) in yearList" :key="index">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="f-b activeColor">{{ item.sysYear }}年</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="text-start font-12">目标销售额:<span class="activeColor">{{ item.targetMoney || 0 }}</span></div>
|
||||
<div class="text-start font-12">年度交易额:<span class="activeColor">{{ item.tradeMoneyYear || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-start font-12 ">
|
||||
完成率:<span class="activeColor">{{ item.finishPer || 0 }}%</span>
|
||||
</div>
|
||||
</van-cell>
|
||||
<van-empty v-else description="暂无数据" />
|
||||
</div>
|
||||
<div v-if="active === 7">
|
||||
<van-cell v-if="accountList?.length" v-for="(item, index) in accountList" :key="index">
|
||||
<div class="f-b text-start balck-text-color mb-5">姓名:{{ item.accountName }}</div>
|
||||
<div class="text-start font-12">账号:{{ item.accountNo }}</div>
|
||||
</van-cell>
|
||||
<van-empty v-else description="暂无数据" />
|
||||
</div>
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { nextTick, ref } from 'vue'
|
||||
const showOverLay = ref(false)
|
||||
const note = ref()
|
||||
const showDetail = (value: string) => {
|
||||
note.value = value
|
||||
showOverLay.value = true
|
||||
}
|
||||
const active = ref(0)
|
||||
const tabs = [{
|
||||
name: 'base',
|
||||
title: '基本信息'
|
||||
}, {
|
||||
name: 'cusUser',
|
||||
title: '员工列表'
|
||||
}, {
|
||||
name: 'work',
|
||||
title: '工作日报'
|
||||
}, {
|
||||
name: 'meeting',
|
||||
title: '参会记录'
|
||||
}, {
|
||||
name: 'communicate',
|
||||
title: '沟通记录'
|
||||
}, {
|
||||
name: 'service',
|
||||
title: '服务目标'
|
||||
}, {
|
||||
name: 'year',
|
||||
title: '年度数据'
|
||||
}, {
|
||||
name: 'account',
|
||||
title: '账号列表'
|
||||
}, {
|
||||
name: '关注股票',
|
||||
title: '关注股票'
|
||||
}]
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
import emitter from '@/utils/mitt'
|
||||
const cusId = ref()
|
||||
const init = () => {
|
||||
if (route.query && route.query.cusId) {
|
||||
cusId.value = route.query.cusId
|
||||
getData()
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
import {
|
||||
cusInfoInfo, cusUserListByCusId, cusLinkList, cusServiceGetByCusId,
|
||||
cusYearList, cusAccountList, cusMeetingList, cusInfoCountByMonth
|
||||
} from '@/utils/api'
|
||||
import { convertToUrl } from '@/utils'
|
||||
const getData = async () => {
|
||||
emitter.emit('showLoading', '')
|
||||
try {
|
||||
getCusInfo()
|
||||
getChartList()
|
||||
getCusUserList()
|
||||
getMeetingList()
|
||||
getCusLinkList()
|
||||
getWorkList()
|
||||
getServiceList()
|
||||
getYearList()
|
||||
getAccountList()
|
||||
} finally {
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
}
|
||||
import * as echarts from 'echarts'
|
||||
const chartLoading = ref(false)
|
||||
const getChartList = async () => {
|
||||
chartLoading.value = false
|
||||
const { data } = await cusInfoCountByMonth({ cusId: cusId.value })
|
||||
const sysDate: any = []
|
||||
const seriesList: any = []
|
||||
const legendList = ['FSH电话会议+调研+其他', '周末会+精英会', '按需会议', '机构路演', '机构沟通', '交易量']
|
||||
const colorList = ['#f26522', '#45b97c', '#009ad6', '#8552a1', '#6c4c49', '#ed1941']
|
||||
legendList.forEach((ele, index) => {
|
||||
if (index < 5) {
|
||||
seriesList.push({
|
||||
type: 'line',
|
||||
name: ele,
|
||||
data: [],
|
||||
yAxisIndex: 0,
|
||||
xAxisIndex: 0,
|
||||
connectNulls: false,
|
||||
symbol: 'none',
|
||||
symbolSize: 0
|
||||
})
|
||||
} else {
|
||||
seriesList.push({
|
||||
type: 'bar',
|
||||
name: ele,
|
||||
data: [],
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
yAxisIndex: 1,
|
||||
xAxisIndex: 1,
|
||||
showEmptyData: false,
|
||||
barWidth: 10
|
||||
})
|
||||
}
|
||||
})
|
||||
data.forEach((ele: any) => {
|
||||
sysDate.push(ele.sysMonth)
|
||||
seriesList[0].data.push(ele.meetingNum34 + ele.meetingNum7)
|
||||
seriesList[1].data.push(ele.meetingNum5 + ele.meetingNum6)
|
||||
seriesList[2].data.push(ele.linkNum31)
|
||||
seriesList[3].data.push(ele.linkNum32)
|
||||
seriesList[4].data.push(ele.linkNum34)
|
||||
seriesList[5].data.push(ele.tradeMoney)
|
||||
})
|
||||
const xAxis = {
|
||||
type: 'category',
|
||||
name: '',
|
||||
data: [],
|
||||
show: true,
|
||||
axisLabel: {
|
||||
show: true,
|
||||
showMaxLabel: true,
|
||||
formatter: '{value}'
|
||||
},
|
||||
gridIndex: 0
|
||||
}
|
||||
const yAxis = {
|
||||
type: 'value',
|
||||
name: '',
|
||||
nameLocation: 'end',
|
||||
scale: true,
|
||||
show: true,
|
||||
axisLabel: {
|
||||
show: true,
|
||||
formatter: '{value}'
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: data.color || '#000'
|
||||
}
|
||||
},
|
||||
position: 'left',
|
||||
}
|
||||
const option = {
|
||||
yAxis: [{ ...yAxis, name: '次数', minInterval: 1 }, { ...yAxis, name: '交易数', minInterval: 1, nameLocation: 'start', gridIndex: 1 }],
|
||||
xAxis: [{ ...xAxis, data: sysDate, name: '时间' }, { ...xAxis, data: sysDate, showFlag: false, gridIndex: 1 }],
|
||||
series: seriesList,
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
formatter: function (data: any) {
|
||||
let res = `${data[0].name}</br>`
|
||||
data.forEach((ele: any) => {
|
||||
res += `<span style="color: ${ele.color}">${ele.seriesName}:${ele.value}${ele.componentSubType === 'bar' ? '百万' : ''}</span></br>`
|
||||
})
|
||||
return res
|
||||
}
|
||||
},
|
||||
legend: { data: legendList, top: '10px' },
|
||||
color: colorList,
|
||||
grid: [{
|
||||
top: '84px',
|
||||
bottom: '35%',
|
||||
left: '40px',
|
||||
right: '40px'
|
||||
}, {
|
||||
top: '72%',
|
||||
bottom: '40px',
|
||||
left: '40px',
|
||||
right: '40px'
|
||||
}]
|
||||
}
|
||||
nextTick(() => {
|
||||
const chart = echarts.init(document.getElementById('meeting_chart'))
|
||||
chart.setOption(option)
|
||||
chartLoading.value = false
|
||||
})
|
||||
}
|
||||
const cusInfo = ref<any>({})
|
||||
const getCusInfo = async () => {
|
||||
const { data } = await cusInfoInfo(cusId.value)
|
||||
cusInfo.value = data
|
||||
emitter.emit('setTitle', { title: cusInfo.value.cusName, type: 'meeting' })
|
||||
}
|
||||
const cusUserList = ref<any[]>([])
|
||||
const getCusUserList = async () => {
|
||||
const { data } = await cusUserListByCusId({ cusId: cusId.value })
|
||||
cusUserList.value = data
|
||||
}
|
||||
const workList = ref<any[]>()
|
||||
const getWorkList = async () => {
|
||||
const url = convertToUrl({
|
||||
limit: 50,
|
||||
cusId: cusId.value,
|
||||
requestFrom: 'DETAIL_RB'
|
||||
})
|
||||
const data = await cusLinkList(url)
|
||||
workList.value = data
|
||||
}
|
||||
const communicateList = ref<any[]>()
|
||||
const getCusLinkList = async () => {
|
||||
const url = convertToUrl({
|
||||
limit: 100,
|
||||
cusId: cusId.value,
|
||||
requestFrom: 'DETAIL_GTJL',
|
||||
dataStatus: 1
|
||||
})
|
||||
const data = await cusLinkList(url)
|
||||
communicateList.value = data
|
||||
}
|
||||
const meetingList = ref<any[]>()
|
||||
const getMeetingList = async () => {
|
||||
const { data } = await cusMeetingList({ cusId: cusId.value, meetingType: '' })
|
||||
meetingList.value = data
|
||||
}
|
||||
const serviceList = ref<any[]>()
|
||||
const getServiceList = async () => {
|
||||
const { data } = await cusServiceGetByCusId({ cusId: cusId.value })
|
||||
data.forEach((ele: any) => {
|
||||
ele.selectId = ele.id
|
||||
ele.serviceStatus = ele.serviceStatus === 0 ? '历史' : '当前'
|
||||
})
|
||||
serviceList.value = data
|
||||
}
|
||||
const yearList = ref<any[]>()
|
||||
const getYearList = async () => {
|
||||
const { data } = await cusYearList({ cusId: cusId.value, curPage: 1, limit: 999 })
|
||||
yearList.value = data.list
|
||||
}
|
||||
const accountList = ref<any[]>()
|
||||
const getAccountList = async () => {
|
||||
const { data } = await cusAccountList({ cusId: cusId.value, curPage: 1, limit: 999 })
|
||||
accountList.value = data.list
|
||||
}
|
||||
init()
|
||||
const changeTab = (num: number) => {
|
||||
if (num === 0) {
|
||||
getChartList()
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$border-color: #e0e0e0;
|
||||
|
||||
.base-info {
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
border: 1px solid $border-color;
|
||||
|
||||
.base-item {
|
||||
font-size: 13px;
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.base-title {
|
||||
width: 30%;
|
||||
border-right: 1px solid $border-color;
|
||||
padding: 6px 10px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.base-desc {
|
||||
background-color: #f2f4f7;
|
||||
padding: 6px 10px;
|
||||
width: 70%;
|
||||
color: #1989fa;
|
||||
}
|
||||
}
|
||||
|
||||
.chartDiv {
|
||||
height: 50vh;
|
||||
width: 100vh;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.van-tab--active) {
|
||||
.van-tab__text {
|
||||
color: #1989fa;
|
||||
}
|
||||
}
|
||||
|
||||
.w-half {
|
||||
width: 50%;
|
||||
}
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
.block {
|
||||
width: 80%;
|
||||
height: 50%;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
112
src/views/organization/list.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="page-content top-select-list" ref="pageContent">
|
||||
<div class="top-select border-bottom-1">
|
||||
<van-dropdown-menu>
|
||||
<van-dropdown-item :title="searchContent" ref="itemRef">
|
||||
<van-field v-model="search" placeholder="搜索" :right-icon="search ? 'clear' : ''"
|
||||
@click-right-icon.stop="clearSearch" />
|
||||
<div style="padding: 5px 16px;">
|
||||
<van-button type="primary" size="small" block round @click="onConfirm">
|
||||
确认
|
||||
</van-button>
|
||||
</div>
|
||||
</van-dropdown-item>
|
||||
<van-dropdown-item v-model="companyType" :options="companyTypeList" @change="onRefresh" />
|
||||
<van-dropdown-item v-model="cusLevel" :options="cusLevelList" @change="onRefresh" />
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" v-if="list.length > 0">
|
||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"
|
||||
:immediate-check="false">
|
||||
<van-cell v-for="item in list" :key="item.cusId"
|
||||
@click="$router.push({ name: 'organization-detail', query: { cusId: item.cusId } })">
|
||||
<div class="name1 text-start stockNames balck-text-color">{{ item.cusName }}</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="font-12">负责人:{{ item.saleUserName }}</div>
|
||||
<div class="name1 text-start flex1">
|
||||
<van-tag :style="{ backgroundColor: item.cusLevelColor}" size="medium">{{ item.companyTypeName }}</van-tag>
|
||||
<van-tag class="tag" size="medium">{{ item.cusLevelName }}</van-tag>
|
||||
</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { cusInfoList, sysDicListByType } from '@/utils/api'
|
||||
|
||||
let searchContent = ref('搜索')
|
||||
let itemRef = ref()
|
||||
const clearSearch = () => {
|
||||
search.value = ''
|
||||
searchContent.value = '搜索'
|
||||
}
|
||||
const onConfirm = () => {
|
||||
searchContent.value = search.value || '搜索'
|
||||
itemRef.value.toggle()
|
||||
onRefresh()
|
||||
}
|
||||
const getData = async () => {
|
||||
const { data } = await cusInfoList({
|
||||
companyType: companyType.value || null,
|
||||
cusLevel: cusLevel.value || null,
|
||||
cusName: search.value,
|
||||
curPage: curPage.value,
|
||||
limit: 20
|
||||
})
|
||||
console.log(data);
|
||||
|
||||
return data
|
||||
}
|
||||
const companyType = ref('')
|
||||
const cusLevel = ref('')
|
||||
const search = ref('')
|
||||
const companyTypeList = ref<any[]>([])
|
||||
const cusLevelList = ref<any[]>([])
|
||||
const getDicList = async () => {
|
||||
const { data } = await sysDicListByType({ dicType: 'sale_company_type' })
|
||||
companyTypeList.value = data.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
companyTypeList.value.unshift({ text: '全部类型', value: '' })
|
||||
const { data: data1 } = await sysDicListByType({ dicType: 'sale_customer_type' })
|
||||
cusLevelList.value = data1.map((ele: any) => {
|
||||
ele.text = ele.dicValue
|
||||
ele.value = ele.dicKey
|
||||
return ele
|
||||
})
|
||||
cusLevelList.value.unshift({ text: '全部评级', value: '' })
|
||||
}
|
||||
getDicList()
|
||||
|
||||
import { listLoadAndRefresh } from '@/mixins/list-load-and-refresh'
|
||||
const { refreshing, finished, loading, curPage, list, onLoad, onRefresh } = listLoadAndRefresh(getData, 'organization')
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
const pageContent = ref()
|
||||
const scrollPosition = ref(0)
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, onRefresh)
|
||||
setScrollTop()
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'organization-detail')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.top-select-list {
|
||||
padding-top: 60px;
|
||||
}
|
||||
.item {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.tag {
|
||||
margin-left: 10px;
|
||||
background-color: #ff758f;
|
||||
}
|
||||
</style>
|
||||
267
src/views/report/list.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<div class="page-content top-select-list">
|
||||
<div class="top-select border-bottom-1">
|
||||
<van-grid :border="false" :gutter="10" :column-num="3">
|
||||
<van-grid-item>
|
||||
<top-select1 :type="'report_type'" :initialName="'类型'" @refresh="refreshByReportType"
|
||||
:selectName="reportTypeName" :selectValue="reportType" />
|
||||
</van-grid-item>
|
||||
<van-grid-item>
|
||||
<top-date1 :initialName="'日期'" @refresh="refreshByDate" :selectDate="date" />
|
||||
</van-grid-item>
|
||||
<van-grid-item>
|
||||
<top-select2 :type="'stock'" :initialName="'股票'" @refresh="refreshByStock" :selectName="stockName"
|
||||
:selectValue="stockCode" />
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
</div>
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" v-if="list.length > 0">
|
||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"
|
||||
:immediate-check="false">
|
||||
<van-cell class="cell" v-for="(item, index) in list" :key="index" :id="item.reportId"
|
||||
@click="showDetail(item, index)">
|
||||
<div class="img-box">
|
||||
<van-image :src="item.url" class="img"></van-image>
|
||||
<div class="title">{{ item.reportName }}</div>
|
||||
</div>
|
||||
<div class="road-info flex-1 text-start d-flex justify-content-between">
|
||||
<div class="title">{{ item.reportName }}</div>
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<span>{{ item.stockName }}</span>
|
||||
<span class="line">|</span>
|
||||
<van-tag size="medium" :class="item.reportColor" class="white-text-color">
|
||||
{{ item.reportTypeName }}
|
||||
</van-tag>
|
||||
</div>
|
||||
<div class="report-desc">
|
||||
<div class="detail text-ellipsis" v-for="(row, idx) in item.reportDescList" :key="idx">{{ row }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="report-time">{{ item.reportDate }}</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick, onUnmounted } from 'vue'
|
||||
import TopSelect1 from '@/components/top-select1.vue'
|
||||
import TopSelect2 from '@/components/top-select2.vue'
|
||||
import TopDate1 from '@/components/top-date1.vue'
|
||||
import emitter from '@/utils/mitt'
|
||||
|
||||
import { stockReportList } from '@/utils/api'
|
||||
import { listLoadAndRefresh } from '@/mixins/list-load-and-refresh'
|
||||
|
||||
const getData = async () => {
|
||||
const { data } = await stockReportList(`${reportType.value ? '?reportType=' + reportType.value : ''}`, {
|
||||
curPage: curPage.value,
|
||||
limit: 20,
|
||||
stockCode: stockCode.value || null,
|
||||
reportDate: date.value !== '日期' ? date.value : null
|
||||
})
|
||||
data.list.map((ele: any) => {
|
||||
ele.url = `/img/${ele.reportType}.jpeg`
|
||||
ele.reportDescList = ele.reportTitle?.split(';') || []
|
||||
ele.reportName = ele.reportName
|
||||
? ele.reportName.substring(0, ele.reportName.length - 4)
|
||||
: ''
|
||||
switch (ele.reportType) {
|
||||
case 'SFBG':
|
||||
ele.reportColor = 'primary-background-color'
|
||||
break
|
||||
case 'GXBG':
|
||||
ele.reportColor = 'danger-background-color'
|
||||
break
|
||||
case 'XLL':
|
||||
ele.reportColor = 'success-background-color'
|
||||
break
|
||||
case 'CLBG':
|
||||
ele.reportColor = 'warning-background-color'
|
||||
break
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
import reportInfoStore from '@/stores/reportInfo'
|
||||
const reportInfo = reportInfoStore()
|
||||
|
||||
const scrollPage = () => {
|
||||
nextTick(() => {
|
||||
if (reportInfo.reportId && reportInfo.index) {
|
||||
stockCode.value = reportInfo.stockCode
|
||||
stockName.value = reportInfo.stockName
|
||||
date.value = reportInfo.date
|
||||
reportTypeName.value = reportInfo.reportTypeName
|
||||
reportType.value = reportInfo.reportType
|
||||
let pageIndex1 = reportInfo.index / 20
|
||||
let pageIndex2 = reportInfo.index % 20
|
||||
if (pageIndex2 > 1) {
|
||||
pageIndex1++
|
||||
}
|
||||
if (pageIndex1 > curPage.value) {
|
||||
onLoad()
|
||||
} else {
|
||||
const el = document.getElementById(reportInfo.reportId)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
reportInfo.reportId = ''
|
||||
reportInfo.index = 0
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
} else {
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
})
|
||||
}
|
||||
const { refreshing, finished, loading, list, onLoad, onRefresh, curPage } = listLoadAndRefresh(getData, 'report', scrollPage)
|
||||
|
||||
import { showFile } from '@/mixins/show-file'
|
||||
import userInfoStore from '@/stores/userInfo'
|
||||
const { loadingFile } = showFile()
|
||||
const showDetail = (item: any, index: number) => {
|
||||
reportInfo.index = index
|
||||
reportInfo.reportId = item.reportId
|
||||
reportInfo.stockCode = stockCode.value
|
||||
reportInfo.stockName = stockName.value
|
||||
reportInfo.date = date.value
|
||||
reportInfo.reportTypeName = reportTypeName.value
|
||||
reportInfo.reportType = reportType.value
|
||||
const userInfo = userInfoStore()
|
||||
loadingFile(`${import.meta.env.VITE_BASE_URL}/stock/report/${item.reportId}?token=${userInfo.token}`)
|
||||
}
|
||||
|
||||
let reportType = ref('')
|
||||
let reportTypeName = ref('类型')
|
||||
let date = ref('日期')
|
||||
let stockCode = ref('')
|
||||
let stockName = ref('股票')
|
||||
|
||||
const refreshByReportType = (e: any) => {
|
||||
reportType.value = e.value
|
||||
reportTypeName.value = e.name
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
const refreshByDate = (e: any) => {
|
||||
date.value = e
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
const refreshByStock = (e: any) => {
|
||||
stockCode.value = e.value
|
||||
stockName.value = e.name
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
const onRefresh1 = () => {
|
||||
stockCode.value = reportInfo.stockCode
|
||||
stockName.value = reportInfo.stockName
|
||||
date.value = reportInfo.date
|
||||
reportTypeName.value = reportInfo.reportTypeName
|
||||
reportType.value = reportInfo.reportType
|
||||
onRefresh()
|
||||
|
||||
}
|
||||
onRefresh1()
|
||||
|
||||
onUnmounted(() => {
|
||||
reportInfo.stockCode = ''
|
||||
reportInfo.stockName = '股票'
|
||||
reportInfo.date = '日期'
|
||||
reportInfo.reportTypeName = '类型'
|
||||
reportInfo.reportType = ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.van-cell {
|
||||
width: 100%;
|
||||
max-width: 678px;
|
||||
margin: 0;
|
||||
box-shadow: 0 0;
|
||||
}
|
||||
|
||||
.cell {
|
||||
:deep(.van-cell__value) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.img-box {
|
||||
width: 30%;
|
||||
max-width: 100PX;
|
||||
max-height: 130PX;
|
||||
position: relative;
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 10px;
|
||||
zoom: 0.8;
|
||||
height: 40px;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.road-info {
|
||||
margin-left: 10px;
|
||||
flex-direction: column;
|
||||
width: 70%;
|
||||
|
||||
.report-time {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.report-desc {
|
||||
color: #999;
|
||||
height: 48px;
|
||||
margin-top: 6px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
|
||||
.detail {
|
||||
line-height: 16px;
|
||||
height: 16px;
|
||||
padding-left: 8px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background-color: #999;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
189
src/views/stock/detail.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="stock-detail" ref="pageContent">
|
||||
<van-steps direction="vertical" :active="-1" v-if="eventList.length > 0" @click-step="showInfo">
|
||||
<van-step v-for="item in eventList" :key="item.id" :id="item.id">
|
||||
<template v-slot:inactive-icon>
|
||||
<div style="background-color: #000" class="slotIcon"></div>
|
||||
</template>
|
||||
<div class="stepItem">
|
||||
<van-row class="mb-16">
|
||||
<van-col span="12">
|
||||
<van-tag size="medium" :stlye="{ backgroundColor: item.eventTypeColor }">
|
||||
{{ item.eventTypeName }}
|
||||
</van-tag>
|
||||
</van-col>
|
||||
<van-col span="12">
|
||||
<div class="text-end tip">{{ item.sysDate }}</div>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<div v-if="item.eventType === 'CHDP'">
|
||||
<div v-if="item.eventNote">
|
||||
<div v-if="!item.playFlag" class="audioDiv d-flex align-items-center">
|
||||
<van-icon name="play-circle" @click="playAudio(item)" />
|
||||
</div>
|
||||
<audio v-else :id="`auto${item.id}`" :src="item.eventNote" controls @play="playAudio(item)"></audio>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eventNote" v-else>{{ item.eventNote }}</div>
|
||||
</div>
|
||||
</van-step>
|
||||
</van-steps>
|
||||
<van-empty v-else />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import emitter from '@/utils/mitt'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
import stockInfoStore from '@/stores/stockInfo'
|
||||
const stockInfo = stockInfoStore()
|
||||
let stockCode: any = ref('')
|
||||
const init = () => {
|
||||
if (route.query && route.query.stockCode) {
|
||||
stockCode.value = route.query.stockCode
|
||||
emitter.emit('showLoading', '')
|
||||
getStockEventList()
|
||||
} else {
|
||||
router.push({ name: '404' })
|
||||
}
|
||||
}
|
||||
let eventList: any = ref([])
|
||||
import { stockStockinfoEventList } from '@/utils/api'
|
||||
const getStockEventList = async () => {
|
||||
const { data }: any = await stockStockinfoEventList({
|
||||
stockCode: stockCode.value
|
||||
})
|
||||
data.map((ele: any) => {
|
||||
ele.playFlag = false
|
||||
})
|
||||
eventList.value = data
|
||||
nextTick(() => {
|
||||
if (stockInfo.id) {
|
||||
const el = document.getElementById(stockInfo.id)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
stockInfo.id = ''
|
||||
emitter.emit('hiddenLoading', '')
|
||||
} else {
|
||||
emitter.emit('hiddenLoading', '')
|
||||
}
|
||||
})
|
||||
}
|
||||
init()
|
||||
|
||||
import { showFile } from '@/mixins/show-file'
|
||||
const { loadingFile } = showFile()
|
||||
const showInfo = (e: any) => {
|
||||
if (eventList.value[e].eventType === 'DP') {
|
||||
router.push({ name: 'comment-detail', query: { inboxId: eventList.value[e].inboxId } })
|
||||
} else if (eventList.value[e].eventType === 'UPDATE' || eventList.value[e].eventType === 'FIRST') {
|
||||
stockInfo.id = eventList.value[e].id
|
||||
loadingFile(`${import.meta.env.VITE_BASE_URL}/api/file/${eventList.value[e].reportFileId}`)
|
||||
}
|
||||
}
|
||||
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
|
||||
let pageContent = ref(null)
|
||||
const scrollPosition = ref(0)
|
||||
|
||||
const refresh = () => {
|
||||
init()
|
||||
}
|
||||
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, refresh)
|
||||
|
||||
setScrollTop()
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'comment-detail')
|
||||
})
|
||||
|
||||
const playAudio = (item: any) => {
|
||||
eventList.value.map((ele: any) => {
|
||||
if (ele.eventType === 'CHDP' && ele.eventNote) {
|
||||
if (item.id === ele.id) {
|
||||
ele.playFlag = true
|
||||
nextTick(() => {
|
||||
const el = document.getElementById(`auto${item.id}`)
|
||||
if (el) {
|
||||
el.play()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (ele.playFlag) {
|
||||
const el = document.getElementById(`auto${item.id}`)
|
||||
if (el) {
|
||||
el.pause()
|
||||
}
|
||||
ele.playFlag = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.stock-detail {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
:deep(.van-step__circle-container) {
|
||||
top: 29px
|
||||
}
|
||||
|
||||
.van-steps--vertical {
|
||||
padding-left: 48px;
|
||||
}
|
||||
|
||||
.van-steps__items {
|
||||
padding-right: 32px
|
||||
}
|
||||
|
||||
.slotIcon {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.stepItem {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.van-step--vertical:not(:last-child):after {
|
||||
border-color: #b2b2b2;
|
||||
margin-right: 46px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.eventNote {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.audioDiv {
|
||||
height: 28px;
|
||||
width: 300px;
|
||||
border-radius: 32px;
|
||||
background-color: #f1f3f4;
|
||||
padding-left: 10px;
|
||||
margin: 4px 0;
|
||||
|
||||
.van-icon {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
audio {
|
||||
height: 28px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
</style>
|
||||
121
src/views/stock/list.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div class="tabbar-list" ref="pageContent">
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" v-if="list.length > 0">
|
||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"
|
||||
:immediate-check="false">
|
||||
<van-cell v-for="item in list" :key="item.stockCode"
|
||||
@click="$router.push({ name: 'stock-detail', query: { stockCode: item.stockCode } })">
|
||||
<van-row class="mb-1">
|
||||
<van-col span="12">
|
||||
<div class="text-start">
|
||||
<van-tag :class="item.eventTypeColor" size="medium">{{ item.eventTypeName }}</van-tag>
|
||||
</div>
|
||||
</van-col>
|
||||
<van-col span="12">
|
||||
<div class="text-end">
|
||||
<div class="sub-title balck-text-color">
|
||||
{{ item.stockCode }}{{ item.stockName ? '【' + item.stockName + '】' : '' }}
|
||||
</div>
|
||||
</div>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<div v-if="item.eventType === 'CHDP'">
|
||||
<div v-if="item.eventNote">
|
||||
<div class="audioDiv d-flex align-items-center">
|
||||
<van-icon name="play-circle" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eventNote text-start" v-else>{{ item.eventNote }}</div>
|
||||
<div class="text-end tip">{{ item.sysDate }}</div>
|
||||
</van-cell>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<van-empty v-else />
|
||||
<tabbar />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { listLoadAndRefresh } from '@/mixins/list-load-and-refresh'
|
||||
import { scrollList } from '@/mixins/scroll-list'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
import Tabbar from '@/components/tabbar.vue'
|
||||
|
||||
let pageContent = ref(null)
|
||||
const scrollPosition = ref(0)
|
||||
|
||||
const refresh = () => {
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
import { stockStockinfoEventNews } from '@/utils/api'
|
||||
|
||||
const getData = async () => {
|
||||
const { data } = await stockStockinfoEventNews({
|
||||
curPage: curPage.value,
|
||||
limit: 20
|
||||
})
|
||||
data.list.map((ele: any) => {
|
||||
switch (ele.eventType) {
|
||||
case 'DP':
|
||||
ele.eventTypeColor = 'primary-background-color'
|
||||
break
|
||||
case 'SFBG':
|
||||
ele.eventTypeColor = 'danger-background-color'
|
||||
break
|
||||
case 'GXBG':
|
||||
ele.eventTypeColor = 'warning-background-color'
|
||||
break
|
||||
case 'XLL':
|
||||
ele.eventTypeColor = 'pink-background-color'
|
||||
break
|
||||
case 'CLBG':
|
||||
ele.eventTypeColor = 'other-background-color'
|
||||
break
|
||||
case 'JOIN':
|
||||
ele.eventTypeColor = 'success-background-color'
|
||||
break
|
||||
case 'CHDP':
|
||||
ele.eventTypeColor = 'purple-background-color'
|
||||
break
|
||||
case 'DATA':
|
||||
ele.eventTypeColor = 'yellow-background-color'
|
||||
break
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
const { refreshing, finished, loading, curPage, list, onLoad, onRefresh } = listLoadAndRefresh(getData, 'stock')
|
||||
const { setScrollTop, setScrollPositionAndRefreshFlag } = scrollList(pageContent, scrollPosition, refresh)
|
||||
|
||||
setScrollTop()
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
setScrollPositionAndRefreshFlag(to, 'stock-detail')
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.eventNote {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.audioDiv {
|
||||
height: 28px;
|
||||
width: 300px;
|
||||
border-radius: 32px;
|
||||
background-color: #f1f3f4;
|
||||
padding-left: 10px;
|
||||
margin: 4px 0;
|
||||
|
||||
.van-icon {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||