版本迭代
31
.gitignore
vendored
@@ -1,9 +1,24 @@
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
/artifacts
|
||||
package-lock.json
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
51
README.md
@@ -1,37 +1,24 @@
|
||||
# temu-plugin
|
||||
# demo
|
||||
|
||||
#### 介绍
|
||||
TEMU助手
|
||||
## Project setup
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
yarn serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
#### 安装教程
|
||||
### Lints and fixes files
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 使用说明
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 参与贡献
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码
|
||||
4. 新建 Pull Request
|
||||
|
||||
|
||||
#### 特技
|
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
||||
5
babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
19
jsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
||||
56
package.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "pdd",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"build-watch": "vue-cli-service --env.NODE_ENV=development build-watch --mode development"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g2plot": "^2.4.31",
|
||||
"axios": "^1.4.0",
|
||||
"core-js": "^3.8.3",
|
||||
"dayjs": "^1.11.9",
|
||||
"element-ui": "^2.15.13",
|
||||
"vue": "^2.6.14",
|
||||
"vue-json-excel": "^0.3.0",
|
||||
"vue-router": "^3.2.0",
|
||||
"vuex": "^3.4.0",
|
||||
"vuex-persistedstate": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"sass": "^1.62.1",
|
||||
"sass-loader": "7.2.0",
|
||||
"vue-cli-plugin-chrome-extension-cli": "~1.1.4",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true,
|
||||
"webextensions": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
BIN
public/favicon_32.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/icons/128.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
public/icons/16.png
Normal file
|
After Width: | Height: | Size: 579 B |
BIN
public/icons/48.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
17
public/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon_32.ico">
|
||||
<title>TEMU助手</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
29
src/api/chromeApi.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import store from '@/store'
|
||||
|
||||
/**
|
||||
* 向Chrome发送消息
|
||||
* @param message 消息
|
||||
*/
|
||||
export function sendChromeAPIMessage(message) {
|
||||
message.type = 'api'
|
||||
message.url = "https://kuajing.pinduoduo.com/" + message.url;
|
||||
if (message.needMallId) {
|
||||
message.mallId = store.state.mallId;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
// @ts-ignore
|
||||
chrome.runtime.sendMessage(message, resolve)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 向Chrome发送消息
|
||||
* @param message 消息
|
||||
*/
|
||||
export function sendChromeNotification(message) {
|
||||
message.type = 'notify'
|
||||
return new Promise((resolve) => {
|
||||
// @ts-ignore
|
||||
chrome.runtime.sendMessage(message, resolve)
|
||||
})
|
||||
}
|
||||
42
src/api/index.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import axios from 'axios'
|
||||
import { Message } from 'element-ui'
|
||||
import store from '@/store'
|
||||
|
||||
|
||||
var instance = axios.create({
|
||||
baseURL: process.env.NODE_ENV === 'production' ? 'http://temu.jjcp52.com' : 'http://temu.jjcp52.com',
|
||||
timeout: 50000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500
|
||||
}
|
||||
})
|
||||
|
||||
instance.interceptors.request.use(request => {
|
||||
if (!request.withoutToken && !request.headers.Authorization) {
|
||||
request.headers.Authorization = store.state.token || ''
|
||||
}
|
||||
|
||||
return request
|
||||
})
|
||||
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
if (response.data.code === 401 || response.data.code === 403) {
|
||||
Message.error('登录失效,请重新登录')
|
||||
store.commit('SignOut', true)
|
||||
|
||||
return response.data
|
||||
} else if (response.data.code === 1 && response.data.msg) {
|
||||
Message.error(response.data.msg)
|
||||
} else {
|
||||
return response.data
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
console.log(err)
|
||||
Message.error('后台报错啦!请联系管理员')
|
||||
return err
|
||||
}
|
||||
)
|
||||
|
||||
export default instance
|
||||
36
src/assets/css/element-variables.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
$--color-primary: #1FBAD6;
|
||||
$borderColor: #DCDFE6 !default;
|
||||
$--color-danger: #FA5555 !default;
|
||||
$--button-active-shade-percent: 20% !default;
|
||||
$--button-hover-tint-percent: 50% !default;
|
||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
||||
|
||||
@import "~element-ui/packages/theme-chalk/src/index";
|
||||
|
||||
.el-form {
|
||||
font-family: SJsuqian;
|
||||
}
|
||||
|
||||
// .el-button:focus {
|
||||
// background: #FFFFFF;
|
||||
// border: 1px solid #DCDFE6;
|
||||
// }
|
||||
.el-button.el-button--primary.is-link:not(.is-disabled):focus {
|
||||
color:var(--el-color-primary)!important;
|
||||
}
|
||||
|
||||
// .el-button.el-button--warning.is-link:not(.is-disabled):focus, .el-button.el-button--warning.is-link:not(.is-disabled):hover{
|
||||
// color:var(--el-color-warning)!important;
|
||||
// }
|
||||
|
||||
// .el-button.el-button--success.is-link:not(.is-disabled):focus, .el-button.el-button--success.is-link:not(.is-disabled):hover{
|
||||
// color:var(--el-color-success)!important;
|
||||
// }
|
||||
|
||||
// .el-button.el-button--info.is-link:not(.is-disabled):focus, .el-button.el-button--info.is-link:not(.is-disabled):hover{
|
||||
// color:var(--el-color-info)!important;
|
||||
// }
|
||||
|
||||
.el-button.el-button--danger.is-link:not(.is-disabled):focus, .el-button.el-button--danger.is-link:not(.is-disabled):hover{
|
||||
color:var(--el-color-danger)!important;
|
||||
}
|
||||
450
src/assets/css/index.scss
Normal file
@@ -0,0 +1,450 @@
|
||||
|
||||
@font-face {
|
||||
font-family: 'iconfont'; /* project id 1995974 */
|
||||
src: url('https://at.alicdn.com/t/font_1995974_ihzpmuv4lpk.eot');
|
||||
src: url('https://at.alicdn.com/t/font_1995974_ihzpmuv4lpk.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_1995974_ihzpmuv4lpk.woff2') format('woff2'),
|
||||
url('https://at.alicdn.com/t/font_1995974_ihzpmuv4lpk.woff') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_1995974_ihzpmuv4lpk.ttf') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_1995974_ihzpmuv4lpk.svg#iconfont') format('svg');
|
||||
}
|
||||
.iconfont{
|
||||
font-family: "iconfont"!important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-stroke-width: 0.2px;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
html {
|
||||
line-height: 1; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
font-family: SJsuqian;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: #B3B3B3;
|
||||
font-size: 14px;
|
||||
font-family: SJsuqian;
|
||||
}
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
legend {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
-webkit-transition: all .3s ease;
|
||||
transition: all .3s ease;
|
||||
}
|
||||
|
||||
ul, li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
img {
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
.middle-container__item {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-align {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-justify_center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.active-wrapper {
|
||||
img {
|
||||
left: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flex1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .3s ease-in-out;
|
||||
}
|
||||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
min-height: calc(100vh - 308px);
|
||||
padding-top: 158px;
|
||||
|
||||
.wrapper-content {
|
||||
width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.hover {
|
||||
cursor: pointer;
|
||||
transition: all ease-in-out .5s;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button {
|
||||
transition: all ease .5s;
|
||||
}
|
||||
|
||||
.el-button--primary:focus {
|
||||
background: #1FBAD6!important;
|
||||
border-color: #1FBAD6!important;
|
||||
}
|
||||
.link-hover {
|
||||
cursor: pointer;
|
||||
transition: all ease .5s;
|
||||
|
||||
&:hover {
|
||||
color: #1FBAD6!important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.info-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 50%;
|
||||
line-height: 17px;
|
||||
margin-bottom: 14px;
|
||||
font-size: 14px;
|
||||
font-family: SJsuqian;;
|
||||
|
||||
label {
|
||||
margin-right: 6px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-disabled {
|
||||
color: #bbb!important;
|
||||
cursor: not-allowed!important;
|
||||
}
|
||||
|
||||
.btn-disabled {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-disabled1 {
|
||||
opacity: 0.3;
|
||||
background: #fff;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.3;
|
||||
color: #1FBAD6!important;
|
||||
background: #fff!important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
color: #fff!important;
|
||||
border-color: #1FBAD6;
|
||||
background-color: #1FBAD6;
|
||||
}
|
||||
|
||||
.el-button--danger:focus, .el-button.el-button--danger.is-link:not(.is-disabled):hover{
|
||||
color: #fff!important;
|
||||
border-color: #FA5555;
|
||||
background-color: #FA5555;
|
||||
}
|
||||
|
||||
.el-input-group__append .el-button {
|
||||
background-color: #1FBAD6!important;
|
||||
}
|
||||
|
||||
.admin-main {
|
||||
.left {
|
||||
&.el-scrollbar .el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-main .el-submenu__title i {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-menu--inline .el-menu-item {
|
||||
padding-left: 55px!important;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none!important;
|
||||
}
|
||||
|
||||
.el-menu .el-menu-item i {
|
||||
color: #fff;
|
||||
}
|
||||
1028
src/assets/css/var.scss
Normal file
BIN
src/assets/images/bitbug_favicon_16.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/images/bitbug_favicon_32.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/assets/images/bitbug_favicon_48.ico
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
src/assets/images/bottom.png
Normal file
|
After Width: | Height: | Size: 197 B |
85
src/assets/images/empty.svg
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="160px" height="160px" viewBox="0 0 160 160" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 58 (84663) - https://sketch.com -->
|
||||
<title>DefaultPage/NoData@2x</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="-1.11022302e-14%" y1="50%" x2="100%" y2="50%" id="linearGradient-1">
|
||||
<stop stop-color="#EEEEEE" stop-opacity="0" offset="0%"></stop>
|
||||
<stop stop-color="#CFCFCF" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="100%" x2="50%" y2="0%" id="linearGradient-2">
|
||||
<stop stop-color="#F2F4F8" offset="0%"></stop>
|
||||
<stop stop-color="#A0A0A0" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="20.244898%" y1="66.7940946%" x2="79.755102%" y2="100%" id="linearGradient-3">
|
||||
<stop stop-color="#E7E7E7" offset="0%"></stop>
|
||||
<stop stop-color="#F2F2F2" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-4">
|
||||
<stop stop-color="#ECECEC" offset="0%"></stop>
|
||||
<stop stop-color="#FFFFFF" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="87.1018058%" y1="60.9391259%" x2="0%" y2="51.9656779%" id="linearGradient-5">
|
||||
<stop stop-color="#EEEEEE" offset="0%"></stop>
|
||||
<stop stop-color="#D8D8D8" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="68.22872%" y1="123.238849%" x2="33.166207%" y2="22.0385879%" id="linearGradient-6">
|
||||
<stop stop-color="#F5F5F5" offset="0%"></stop>
|
||||
<stop stop-color="#D8D8D8" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="0%" y1="0%" x2="0%" y2="100%" id="linearGradient-7">
|
||||
<stop stop-color="#F2F2F2" offset="0%"></stop>
|
||||
<stop stop-color="#D6D6D6" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="0%" y1="39.2801795%" x2="16.8660114%" y2="70.0555556%" id="linearGradient-8">
|
||||
<stop stop-color="#E9E9E9" offset="0%"></stop>
|
||||
<stop stop-color="#F0F0F0" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-9">
|
||||
<stop stop-color="#5777D4" offset="0%"></stop>
|
||||
<stop stop-color="#101B3E" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="81.4402452%" y2="56.408967%" id="linearGradient-10">
|
||||
<stop stop-color="#164898" offset="0%"></stop>
|
||||
<stop stop-color="#223762" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M0.692684439,9.43167978 C1.038713,9.68406083 1.46715785,9.03996696 1.92500086,8.75189963 C2.82917603,8.1830074 3.05602226,6.14182671 2.99566429,6.00418021 C2.86370082,5.70323714 1.63363165,4.13284399 1.75185981,3.629798 C1.87008797,3.12675201 2.75996609,4.2529312 2.71853333,3.9450838 C2.67710057,3.63723639 1.65860147,2.14944807 2.03173003,1.61764841 C2.4048586,1.08584875 3.20874322,0.781310852 3.46157268,1.61764841 C4.0498272,1.9917884 4.68398473,1.05714111 5.01137544,0.781310852 C5.33876615,0.505480598 6.04721636,0.06510516 6.81229001,0.06510516 C7.57736365,0.06510516 8.5052774,0.131064853 8.52936011,0.310000564 C8.55344283,0.488936275 9.85049474,3.58207339 9.42444215,3.629798 C8.99838956,3.67752261 8.55839311,3.84551454 8.52936011,3.629798 C8.50032712,3.41408147 8.52395619,5.5702089 8.00031054,6.16034326 C7.59261269,6.61980761 6.90464724,6.61980761 6.81229001,6.70051253 C6.92864366,6.92703467 8.00682266,10.9089185 9.42444215,10.9099572 C10.8420616,10.910996 10.5339947,16.4526524 8.52936011,15.7958809 C7.0572883,15.313591 4.70544072,14.5952614 1.47381736,13.640892 C-0.140677622,11.7612238 -0.401055261,10.3968483 0.692684439,9.54776539 C0.745126983,9.50705363 0.301852492,9.58449006 0.355223454,9.54776539 L0.692684439,9.43167978 Z" id="path-11"></path>
|
||||
<linearGradient x1="81.2218693%" y1="3.46944695e-16%" x2="18.7781307%" y2="105.693594%" id="linearGradient-13">
|
||||
<stop stop-color="#0074FF" offset="0%"></stop>
|
||||
<stop stop-color="#009BFF" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="DefaultPage/NoData" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M87,133 C32.3333333,129.212407 5,123.679002 5,116.399786 C5,105.480961 56.1964855,98.5697269 59.4353967,101.797298 C61.5946709,103.949012 70.7828719,114.349912 87,133 Z" id="路径-10" fill="url(#linearGradient-1)"></path>
|
||||
<g id="编组-6" transform="translate(25.000000, 55.000000)">
|
||||
<polygon id="矩形" fill="url(#linearGradient-2)" opacity="0.600000024" points="8 8 62 -8.79504802e-15 62 78 8 55"></polygon>
|
||||
<polygon id="矩形" fill="url(#linearGradient-3)" points="8 8 62 14 62 78 8 55"></polygon>
|
||||
<polygon id="矩形" fill="url(#linearGradient-4)" points="14 14 56 23 56 67 14 50"></polygon>
|
||||
<polygon id="矩形" fill="url(#linearGradient-5)" points="8 8 62 14 54 37 -2.27366737e-13 25"></polygon>
|
||||
<polygon id="矩形" fill="url(#linearGradient-6)" points="112 5 62 -8.79504802e-15 62 78 112 55"></polygon>
|
||||
<polygon id="矩形" fill="url(#linearGradient-7)" points="112 5 62 14 62 78 112 55"></polygon>
|
||||
<polygon id="矩形" fill="url(#linearGradient-8)" points="112 5 62 14 72 43 122 25"></polygon>
|
||||
</g>
|
||||
<g id="编组-8" transform="translate(99.000000, 29.000000)">
|
||||
<path d="M7.01014176,64 C5.81563314,64 5.39853322,64.9231563 5.98625682,67.1419148 C6.57398041,69.3606732 6.57398041,73.099898 8.21442955,73.6840461 C9.85487869,74.2681943 10.2675495,72.4172628 10.0306673,70.2729153 C9.79378519,68.1285679 10.5969285,64.8486097 10.0306673,64.8486097 C9.46440621,64.8486097 8.20465038,64 7.01014176,64 Z" id="路径-23" fill="#27234B"></path>
|
||||
<path d="M8.67858722,60.8068485 C7.57745052,63.7962121 7.02688217,65.515603 7.02688217,65.9650211 C7.02688217,66.6391482 8.14770042,67.9959336 8.67858722,67.7290608 C9.03251176,67.5511456 9.82225562,65.4781591 11.0478188,61.5101011 L8.67858722,60.8068485 Z" id="路径-24" fill="#FFD6A7"></path>
|
||||
<path d="M17.6525712,58.3128036 C16.4580626,58.3128036 15.5791956,59.5723043 16.1669192,61.7910627 C16.7546428,64.0098211 17.1206498,67.1792185 18.7610989,67.7633667 C20.4015481,68.3475148 20.5184284,66.429455 20.2815463,64.2851076 C20.0446641,62.1407602 20.4116365,58.7211343 19.8453754,58.7211343 C19.2791143,58.7211343 18.8470799,58.3128036 17.6525712,58.3128036 Z" id="路径-23" fill="#27234B"></path>
|
||||
<path d="M19.0373505,54.8345445 C17.9362138,57.8239082 17.3856455,59.5432991 17.3856455,59.9927171 C17.3856455,60.6668442 18.5064637,62.0236296 19.0373505,61.7567569 C19.391275,61.5788417 20.1810189,59.5058551 21.4065821,55.5377972 L19.0373505,54.8345445 Z" id="路径-24" fill="#FFD6A7"></path>
|
||||
<path d="M6.83909623,33.9320553 C6.05741636,38.4828718 11.5516213,43.9593332 11.5516213,47.2130169 C11.5516213,50.4667006 6.71818349,62.5891228 7.18189188,63.8904979 C7.64560028,65.1918731 9.53550857,66.7841621 10.2070863,65.7711218 C10.8786639,64.7580814 18.8563568,48.1367918 18.8563568,45.6549977 C18.8563568,43.1732035 15.8102723,39.9517455 16.595327,39.1690119 C17.3803817,38.3862783 21.1620037,42.8944111 21.1620037,44.4578151 C21.1620037,46.021219 17.6522108,56.7723936 18.0269429,57.9559074 C18.401675,59.1394212 20.3181472,59.7449758 20.8053247,58.9477997 C21.2925022,58.1506236 28.5668954,45.0375795 27.8295527,42.1054879 C27.0922099,39.1733962 21.2623277,32.5968868 19.8162693,32.5968868 C18.3702109,32.5968868 7.6207761,29.3812388 6.83909623,33.9320553 Z" id="路径-20" fill="url(#linearGradient-9)"></path>
|
||||
<g id="编组-7" transform="translate(8.722445, 0.000000)">
|
||||
<path d="M4.30855025,10.9942019 C3.35685956,10.810352 1.77386943,8.86553992 1.18202581,7.80054593 C0.81387515,7.13807662 0,5.96438949 0,4.61947329 C0,3.27455708 1.10573563,1.77660133 1.92584455,2.48051045 C1.92584455,0.20260789 7.956092,-0.67811348 9.65271137,0.566356728 C11.3493307,1.81082694 11.5461677,4.61947329 9.65271137,4.61947329 C7.75925506,4.61947329 8.65716044,5.86566186 6.49698059,6.39903511 C4.33680074,6.93240835 5.26024095,11.1780519 4.30855025,10.9942019 Z" id="路径-45" fill="url(#linearGradient-10)"></path>
|
||||
<g id="路径-46" transform="translate(0.000000, 4.000000)">
|
||||
<mask id="mask-12" fill="white">
|
||||
<use xlink:href="#path-11"></use>
|
||||
</mask>
|
||||
<use id="Mask" fill="#FFD6A7" xlink:href="#path-11"></use>
|
||||
<path d="M6.75437134,6.70359449 C6.31581737,6.70359449 4.23395853,6.06113278 4.07998644,5.91934988 C3.92601435,5.77756697 4.30907101,7.95013098 7.03325627,7.24651446 C7.13970229,6.88456781 7.04674065,6.70359449 6.75437134,6.70359449 Z" fill="#F0A53D" style="mix-blend-mode: multiply;" opacity="0.5" mask="url(#mask-12)"></path>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M5.07311779,23.0073693 C4.68808094,24.5256706 3.22544506,29.9836152 3.22544506,30.9108125 C3.22544506,31.8380098 2.92548182,40.2897051 2.92548182,40.732508 C2.92548182,41.1753109 3.95854849,42.4516251 3.22544506,43.0507113 C2.49234162,43.6497976 1.81188398e-13,43.8485127 1.81188398e-13,42.6287042 C1.81188398e-13,41.4088957 0.388607838,40.8243035 0.388607838,40.400826 C0.388607838,39.9773485 0.0426418331,30.584592 0.388607838,29.2082901 C0.734573844,27.8319881 0.991265075,22.1473907 1.40697043,22.1473907 C1.82267579,22.1473907 5.45815463,21.489068 5.07311779,23.0073693 Z" id="路径-21" fill="#FFD6A7"></path>
|
||||
<path d="M19.7224453,18.0973977 C21.3690975,22.7100392 22.2650744,25.5022607 22.4103758,26.474062 C22.8314797,29.2904788 23.8498607,34.1020694 23.6765651,35.4562685 C23.6120851,35.9601407 22.9354543,34.8946698 22.4103758,34.6262885 C22.1090393,34.4722675 22.4103758,35.9216374 21.5628858,35.0855846 C20.7153959,34.2495318 20.3888906,31.3824333 19.3961138,28.7784414 C18.342038,26.0136663 18.3283189,21.5355261 18,20.6187136 C17.5749702,19.4318413 18.1491186,18.5914027 19.7224453,18.0973977 Z" id="路径-22" fill="#FFD6A7"></path>
|
||||
<path d="M4.9703812,15.6721901 C2.78306286,18.2789727 0.132716487,22.6538702 1.45788967,23.1495059 C2.34133846,23.4799296 3.51216897,23.6121861 4.9703812,23.5462753 C4.3797922,33.6429819 4.72325901,38.9853255 6.00078161,39.5733058 C7.91706551,40.4552764 7.91706551,36.3848035 13.2610612,35.0974552 C18.6050568,33.8101068 21.0174981,34.5887765 21.885643,33.5544631 C22.753788,32.5201498 18.4263671,33.4138591 18.4263671,28.6761041 C18.4263671,23.9383491 19.0106954,23.2361117 21.2666202,22.1723211 C23.5225449,21.1085305 18.0095821,13.2694007 16.5633695,13.2694007 C16.0714732,13.2694007 15.200047,14.1569229 12.9725581,14.1569229 C11.2453371,14.1569229 10.5766396,13.099353 9.28971537,13.2694007 C7.42163987,13.5162388 5.82498987,14.6536923 4.9703812,15.6721901 Z" id="路径-8" fill="url(#linearGradient-13)" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/assets/images/kjfs.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/assets/images/right-b.png
Normal file
|
After Width: | Height: | Size: 182 B |
BIN
src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src/assets/qrcode.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
333
src/components/AiArticle.vue
Normal file
@@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<div class="ai-article">
|
||||
<div v-html="value"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiArticle',
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-article {
|
||||
width: 100%;
|
||||
line-height: 1.75;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
text-align: justify;
|
||||
overflow-x: auto;
|
||||
word-break: break-word;
|
||||
|
||||
:deep( h1 ){
|
||||
margin: 1.3rem 0;
|
||||
line-height: 1.2
|
||||
}
|
||||
|
||||
:deep( p ){
|
||||
line-height: 2.27rem
|
||||
}
|
||||
|
||||
:deep( hr ){
|
||||
border: none;
|
||||
border-top: 1px solid #ddd;
|
||||
margin-top: 2.7rem;
|
||||
margin-bottom: 2.7rem
|
||||
}
|
||||
|
||||
:deep( img:not(.equation)), :deep( iframe), :deep( embed), :deep( video ){
|
||||
display: block;
|
||||
margin: 18px auto;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
:deep( img.equation ){
|
||||
margin: 0 .1em;
|
||||
max-width: 100% !important;
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
:deep( figure ){
|
||||
margin: 2.7rem auto;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
:deep( img:not(.equation) ){
|
||||
cursor: zoom-in
|
||||
}
|
||||
|
||||
:deep( figure figcaption ){
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
line-height: 2.7rem;
|
||||
color: #909090
|
||||
}
|
||||
|
||||
:deep( pre ){
|
||||
line-height: 1.93rem;
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
:deep( code),
|
||||
:deep( pre ){
|
||||
font-family: Menlo, Monaco, Consolas, Courier New, monospace
|
||||
}
|
||||
|
||||
:deep( code ){
|
||||
font-size: 1rem;
|
||||
padding: .26rem .53em;
|
||||
word-break: break-word;
|
||||
color: #4e5980;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 2px;
|
||||
overflow-x: auto
|
||||
}
|
||||
|
||||
:deep( pre>code ){
|
||||
font-size: 1rem;
|
||||
padding: .67rem 1.3rem;
|
||||
margin: 0;
|
||||
word-break: normal;
|
||||
display: block
|
||||
}
|
||||
|
||||
:deep( a ){
|
||||
color: #259
|
||||
}
|
||||
|
||||
:deep( a:active),
|
||||
:deep( a:hover ){
|
||||
color: #275b8c
|
||||
}
|
||||
|
||||
:deep( table ){
|
||||
width: 100%;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 18px;
|
||||
overflow: auto;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
border-top: 1px solid #ccc;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
:deep( thead ){
|
||||
background: #f6f6f6;
|
||||
color: #000;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
:deep( td),
|
||||
:deep( th ){
|
||||
padding: 3px 5px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
:deep( td ){
|
||||
min-width: 10rem
|
||||
}
|
||||
|
||||
:deep( blockquote ){
|
||||
margin: 1em 0;
|
||||
border-left: 4px solid #ddd;
|
||||
padding: 0 1.3rem
|
||||
}
|
||||
|
||||
:deep( blockquote>p ){
|
||||
margin: .6rem 0
|
||||
}
|
||||
|
||||
:deep( ol),
|
||||
:deep( ul ){
|
||||
padding-left: 2.7rem
|
||||
}
|
||||
|
||||
:deep( ol li),
|
||||
:deep( ul li ){
|
||||
margin-bottom: .6rem
|
||||
}
|
||||
|
||||
:deep( ol ol),
|
||||
:deep( ol ul),
|
||||
:deep( ul ol),
|
||||
:deep( ul ul ){
|
||||
margin-top: .27rem
|
||||
}
|
||||
|
||||
:deep( pre>code ){
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
color: #333;
|
||||
background: #f8f8f8
|
||||
}
|
||||
|
||||
:deep( p ){
|
||||
line-height: inherit;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 18px
|
||||
}
|
||||
:deep( p:first-child){
|
||||
margin-top: 0!important;
|
||||
}
|
||||
:deep( img ){
|
||||
max-height: none
|
||||
}
|
||||
|
||||
:deep( a ){
|
||||
color: #0269c8;
|
||||
border-bottom: 1px solid #d1e9ff
|
||||
}
|
||||
|
||||
:deep( code ){
|
||||
background-color: #fff5f5;
|
||||
color: #ff502c;
|
||||
font-size: .87em;
|
||||
padding: .065em .4em
|
||||
}
|
||||
|
||||
:deep( blockquote ){
|
||||
color: #666;
|
||||
padding: 1px 23px;
|
||||
margin: 18px 0;
|
||||
border-left: 4px solid #cbcbcb;
|
||||
background-color: #f8f8f8
|
||||
}
|
||||
|
||||
:deep( blockquote:after ){
|
||||
display: block;
|
||||
content: ""
|
||||
}
|
||||
|
||||
:deep( blockquote>p ){
|
||||
margin: 10px 0
|
||||
}
|
||||
|
||||
:deep( blockquote.warning ){
|
||||
position: relative;
|
||||
border-left-color: #f75151;
|
||||
margin-left: 8px
|
||||
}
|
||||
|
||||
:deep( blockquote.warning:before ){
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: -12px;
|
||||
background: #f75151;
|
||||
border-radius: 50%;
|
||||
content: "!";
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center
|
||||
}
|
||||
|
||||
:deep( ol),
|
||||
:deep( ul ){
|
||||
padding-left: 28px
|
||||
}
|
||||
|
||||
:deep( ol li),
|
||||
:deep( ul li ){
|
||||
margin-bottom: 0;
|
||||
list-style: inherit
|
||||
}
|
||||
|
||||
:deep( ol li.task-list-item),
|
||||
:deep( ul li.task-list-item ){
|
||||
list-style: none
|
||||
}
|
||||
|
||||
:deep( ol li.task-list-item ol),
|
||||
:deep( ol li.task-list-item ul),
|
||||
:deep( ul li.task-list-item ol),
|
||||
:deep( ul li.task-list-item ul ){
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
:deep( ol li ){
|
||||
padding-left: 6px
|
||||
}
|
||||
|
||||
:deep( pre ){
|
||||
position: relative;
|
||||
line-height: 1.75
|
||||
}
|
||||
|
||||
:deep( pre>code ){
|
||||
padding: 15px 12px
|
||||
}
|
||||
|
||||
:deep( pre>code.hljs[lang] ){
|
||||
padding: 18px 15px 12px
|
||||
}
|
||||
|
||||
:deep( h1),
|
||||
:deep( h2),
|
||||
:deep( h3),
|
||||
:deep( h4),
|
||||
:deep( h5),
|
||||
:deep( h6 ){
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep( h1 ){
|
||||
font-size: 30px;
|
||||
margin-bottom: 5px
|
||||
}
|
||||
|
||||
:deep( h2 ){
|
||||
padding-bottom: 12px;
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #ececec
|
||||
}
|
||||
|
||||
:deep( h3 ){
|
||||
font-size: 18px;
|
||||
padding-bottom: 0
|
||||
}
|
||||
|
||||
:deep( h4 ){
|
||||
font-size: 16px
|
||||
}
|
||||
|
||||
:deep( h5 ){
|
||||
font-size: 15px
|
||||
}
|
||||
|
||||
:deep( h6 ){
|
||||
margin-top: 5px
|
||||
}
|
||||
|
||||
:deep( h1.heading+h2.heading ){
|
||||
margin-top: 20px
|
||||
}
|
||||
|
||||
:deep( h1.heading+h3.heading ){
|
||||
margin-top: 15px
|
||||
}
|
||||
|
||||
:deep( .heading+.heading ){
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
:deep( h1+:not(.heading) ){
|
||||
margin-top: 25px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
114
src/components/AiBar.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="aibar" :style="{ marginBottom }" :class="{'aibar-center':titlePosition === 'center',card}">
|
||||
<div v-if="titlePosition === 'center'"/>
|
||||
<div class="aibar-left" :class="[titlePosition === 'center' ? 'aibar-left__center' : '']">
|
||||
<template v-if="!$slots.title">{{ title }}</template>
|
||||
<slot name="title" v-else></slot>
|
||||
</div>
|
||||
<div class="aibar-right">
|
||||
<slot v-if="$slots.right" name="right"/>
|
||||
<slot v-else/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiBar',
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
|
||||
customCliker: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
marginBottom: {
|
||||
type: String,
|
||||
default: '16px'
|
||||
},
|
||||
|
||||
titlePosition: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
card: Boolean
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.aibar {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 56px;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
||||
|
||||
&.card {
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
}
|
||||
|
||||
.aibar-left {
|
||||
color: #222;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.aibar-left__center {
|
||||
position: relative;
|
||||
width: 556px;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.aibar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #5088FF;
|
||||
font-size: 12px;
|
||||
|
||||
i {
|
||||
line-height: 1;
|
||||
color: #5088FF;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
& > div, & > a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aibar-center {
|
||||
height: auto;
|
||||
padding: 10px 0;
|
||||
|
||||
h2 {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
64
src/components/AiCard.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<section class="ai-card" :class="{panel,headerPanel}">
|
||||
<ai-bar v-if="!hideHeader" :title="title" v-bind="$attrs">
|
||||
<template #title>
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
<template #right>
|
||||
<slot name="right"></slot>
|
||||
</template>
|
||||
</ai-bar>
|
||||
<div class="ai-card__body" :style="{paddingTop: hideHeader ? '20px' : 0}">
|
||||
<slot v-if="$scopedSlots.content" name="content"/>
|
||||
<slot v-else/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiCard',
|
||||
props: {
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
hideTitle: Boolean,
|
||||
panel: Boolean,
|
||||
headerPanel: Boolean
|
||||
},
|
||||
computed: {
|
||||
hideHeader: v => v.hideTitle || (v.panel && !v.title)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.ai-card {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
background: #FFFFFF;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 2px;
|
||||
|
||||
.ai-card__body {
|
||||
height: calc(100% - 72px);
|
||||
padding: 0 20px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&.panel, &.headerPanel {
|
||||
margin-bottom: 0;
|
||||
|
||||
.ai-card__body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.aibar {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
131
src/components/AiDetail.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="ai-detail">
|
||||
<div class="ai-detail__title">
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
<div class="ai-detail__step" v-if="isHasStepSlot">
|
||||
<slot name="step"></slot>
|
||||
</div>
|
||||
<div class="ai-detail__content" :class="className">
|
||||
<div class="ai-detail__content--wrapper" :class="{'ai-detail__content--side':isHasSidebar,list}">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-detail__footer" v-if="isHasFooterSlot">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiDetail',
|
||||
|
||||
props: {
|
||||
isHasSidebar: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
list: Boolean
|
||||
},
|
||||
|
||||
computed: {
|
||||
isHasFooterSlot() {
|
||||
return this.$slots.footer
|
||||
},
|
||||
|
||||
isHasStepSlot() {
|
||||
return this.$slots.step
|
||||
},
|
||||
|
||||
className() {
|
||||
if (this.isHasFooterSlot) {
|
||||
if (this.isHasStepSlot) {
|
||||
return 'ai-detail__content--active-step'
|
||||
}
|
||||
|
||||
return 'ai-detail__content--active'
|
||||
} else {
|
||||
if (this.isHasStepSlot) {
|
||||
return 'ai-detail__content--step'
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-detail {
|
||||
height: 100%;
|
||||
background: #F5F6F9;
|
||||
overflow: hidden;
|
||||
|
||||
.ai-detail__title {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.ai-detail__step {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.ai-detail__content {
|
||||
height: calc(100% - 48px);
|
||||
padding: 0 0 20px 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: overlay;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1740px) {
|
||||
.ai-detail__content {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-detail__content--wrapper {
|
||||
position: relative;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
|
||||
&.list {
|
||||
max-width: unset;
|
||||
margin: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1740px) {
|
||||
.ai-detail__content--side {
|
||||
margin-left: 128px;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-detail__content--active {
|
||||
height: calc(100% - 48px - 64px);
|
||||
}
|
||||
|
||||
.ai-detail__content--active-step {
|
||||
height: calc(100% - 48px - 64px - 72px);
|
||||
}
|
||||
|
||||
.ai-detail__content--step {
|
||||
height: calc(100% - 48px - 72px);
|
||||
}
|
||||
|
||||
.ai-detail__footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
background: #F5F5F5;
|
||||
box-shadow: 0px 1px 0px 0px #E5E5E5;
|
||||
|
||||
.el-button {
|
||||
width: 92px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
121
src/components/AiDialog.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<section class="ai-dialog__wrapper">
|
||||
<el-dialog custom-class="ai-dialog" v-on="$listeners" v-bind="$attrs" :visible.sync="dialog">
|
||||
<div class="ai-dialog__header fill" slot="title" v-text="title"/>
|
||||
<div class="ai-dialog__content">
|
||||
<div class="ai-dialog__content--wrapper pad-r8">
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="customFooter" slot="footer">
|
||||
<slot name="footer"/>
|
||||
</template>
|
||||
<div v-else class="dialog-footer" slot="footer">
|
||||
<el-button @click="onCancel">取消</el-button>
|
||||
<el-button @click="onConfirm" type="primary">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'AiDialog',
|
||||
model: {
|
||||
prop: "visible",
|
||||
event: "input"
|
||||
},
|
||||
props: {
|
||||
visible: Boolean,
|
||||
title: {type: String, default: ''},
|
||||
customFooter: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dialog(v) {
|
||||
this.visible != v && this.$emit("input", v)
|
||||
},
|
||||
visible(v) {
|
||||
this.dialog = v
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCancel() {
|
||||
this.$emit('input', false)
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('cancel')
|
||||
},
|
||||
onConfirm() {
|
||||
this.$emit('confirm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.ai-dialog {
|
||||
margin: unset !important;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 20px 40px 20px;
|
||||
}
|
||||
|
||||
.ai-dialog__content {
|
||||
overflow-y: auto;
|
||||
padding-bottom: 4px;
|
||||
max-height: 500px;
|
||||
|
||||
.ai-dialog__content--wrapper {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-dialog__header {
|
||||
flex: 1;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0 16px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 16px 20px;
|
||||
box-sizing: border-box;
|
||||
background: #F3F6F9;
|
||||
text-align: center;
|
||||
|
||||
& + .el-button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
width: 92px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
top: unset;
|
||||
right: unset;
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
34
src/components/AiEmpty.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="ai-empty">
|
||||
<div class="ai-empty__bg"></div>
|
||||
<template v-if="!isHasTitleSlot">暂无数据</template>
|
||||
<slot v-else></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiEmpty',
|
||||
|
||||
computed: {
|
||||
isHasTitleSlot () {
|
||||
return this.$slots.default
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.ai-empty {
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
color: #acaaad;
|
||||
|
||||
.ai-empty__bg {
|
||||
background: url("../assets/images/empty.svg") no-repeat center;
|
||||
background-size: 120px 120px;
|
||||
height: 120px;
|
||||
margin: 48px auto 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
91
src/components/AiInfoItem.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="ai-info-item" :style="contentStyle">
|
||||
<label class="ai-info-item__left" :style="contentLabelStyle">
|
||||
<template v-if="$slots.label">
|
||||
<slot name="label"/>
|
||||
</template>
|
||||
<template v-else>{{ label }}</template>
|
||||
</label>
|
||||
<div class="ai-info-item__right">
|
||||
<slot v-if="$scopedSlots.default"/>
|
||||
<template v-else-if="!!openType">
|
||||
<ai-open-data :type="openType" :openid="value"/>
|
||||
</template>
|
||||
<template v-else>{{ value || value === 0 ? value : '-' }}</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiInfoItem',
|
||||
|
||||
inject: ['AiWrapper'],
|
||||
|
||||
props: {
|
||||
label: {
|
||||
type: String
|
||||
},
|
||||
|
||||
value: {
|
||||
type: [String, Number]
|
||||
},
|
||||
|
||||
'label-width': {
|
||||
type: String
|
||||
},
|
||||
|
||||
isLine: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
openType: {default: ""}
|
||||
},
|
||||
|
||||
computed: {
|
||||
contentStyle() {
|
||||
let width = this.AiWrapper.autoWidth
|
||||
|
||||
if (this.isLine) {
|
||||
width = '100%'
|
||||
}
|
||||
|
||||
return {
|
||||
width
|
||||
}
|
||||
},
|
||||
|
||||
contentLabelStyle() {
|
||||
return {
|
||||
width: this.labelWidth || this.AiWrapper.autoLableWidth
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-info-item {
|
||||
display: flex;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 16px;
|
||||
|
||||
label {
|
||||
flex-shrink: 0;
|
||||
width: 96px;
|
||||
margin-right: 40px;
|
||||
text-align: right;
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ai-info-item__right {
|
||||
flex: 1;
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
263
src/components/AiList.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="ai-list" :class="listClass">
|
||||
<div class="ai-list__title" v-if="$slots.title">
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
<div class="ai-list__tabs" v-if="$slots.tabs">
|
||||
<slot name="tabs"></slot>
|
||||
</div>
|
||||
<el-row type="flex" class="ai-list__blank" v-else-if="$scopedSlots.blank" :class="{hasLeft:$scopedSlots.left}">
|
||||
<div class="ai-list__content--left" v-if="$scopedSlots.left">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<div class="fill">
|
||||
<slot name="blank"/>
|
||||
</div>
|
||||
</el-row>
|
||||
<div class="ai-list__content" v-else :class="contentClass">
|
||||
<div class="ai-list__content--wrapper">
|
||||
<slot name="custom" v-if="!!$slots.custom"/>
|
||||
<template v-else>
|
||||
<div class="ai-list__content--left" v-if="$slots.left">
|
||||
<slot name="left"></slot>
|
||||
</div>
|
||||
<div class="ai-list__content--right" :style="{width: !$slots.left ? '100%' : 'auto' }">
|
||||
<div class="ai-list__content--right-wrapper" :style="{minHeight: $slots.left ? '100%' : 'auto' }">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiList',
|
||||
props: {
|
||||
isTabs: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
listClass() {
|
||||
if (this.$slots.left || this.$slots.tabs) {
|
||||
return 'ai-left__list'
|
||||
}
|
||||
|
||||
if (this.isTabs) {
|
||||
return 'ai-left__list ai-list__notab'
|
||||
}
|
||||
|
||||
if (!this.isTabs && !this.$slots.tabs && !this.$slots.left) {
|
||||
return 'ai-list__single'
|
||||
}
|
||||
|
||||
return ''
|
||||
},
|
||||
|
||||
contentClass() {
|
||||
if (this.isTabs) {
|
||||
return 'ai-list__tab--content'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//全局tab css
|
||||
:deep( .ai-list__tabs ) {
|
||||
margin-top: 0 !important;
|
||||
|
||||
.el-tabs__item {
|
||||
position: relative;
|
||||
width: auto !important;;
|
||||
padding: 0 16px !important;
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap::after {
|
||||
height: 1px;
|
||||
background-color: #D8DCE3;
|
||||
}
|
||||
|
||||
.el-tabs__active-bar {
|
||||
// left: -16px;
|
||||
// width: 50%!important;
|
||||
}
|
||||
|
||||
.el-tabs__nav {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.el-tabs__header {
|
||||
padding: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.el-tab-pane {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
overflow: hidden;
|
||||
background: #f3f6f9;
|
||||
|
||||
&.ai-list__single {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
|
||||
.ai-list__title {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.ai-list__content {
|
||||
flex: 1;
|
||||
padding: 20px 20px 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ai-list__content--wrapper {
|
||||
height: 100%;
|
||||
margin: 0 !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ai-list__content--right-wrapper {
|
||||
// margin: 0 20px;
|
||||
padding: 20px 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ai-list__tabs {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
:deep( .el-tabs__item ) {
|
||||
min-width: 80px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
color: #222;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__header ) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs ) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content ) {
|
||||
height: calc(100% - 32px);
|
||||
background: #f3f6f9;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list__content {
|
||||
.ai-list__content--wrapper {
|
||||
display: flex;
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list__content--left {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
margin-right: 20px;
|
||||
padding-left: 1px;
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
min-width: 264px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.ai-list__content--right {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
.ai-list__content--right-wrapper {
|
||||
padding: 12px 16px 12px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list__tab--content {
|
||||
height: 100% !important;
|
||||
|
||||
& > div {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ai-list__notab {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ai-list__blank {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
|
||||
&.hasLeft {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
& > .fill {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-left__list {
|
||||
height: 100%;
|
||||
background: #f3f6f9;
|
||||
overflow: hidden;
|
||||
|
||||
.ai-list__content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.ai-list__content--wrapper {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
|
||||
.ai-list__content--right {
|
||||
// margin-left: 11px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
76
src/components/AiPullDown.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<section class="AiPullDown">
|
||||
<div class="line"/>
|
||||
<div class="down-content" @click="handleExpand">
|
||||
<i :class="expandIcon"/>
|
||||
<span>{{ btnText }}</span>
|
||||
</div>
|
||||
<div class="line"/>
|
||||
</section>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiPullDown",
|
||||
props: {
|
||||
target: String,
|
||||
height: {default: 4},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expand: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleExpand() {
|
||||
this.expand = !this.expand
|
||||
if (this.target) {
|
||||
|
||||
} else this.$emit('change', this.expandStyle)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
btnText() {
|
||||
return this.expand ? '收起高级搜索' : '展开高级搜索'
|
||||
},
|
||||
expandStyle() {
|
||||
let initStyle = {overflow: 'hidden', height: `${this.height}px`}
|
||||
this.expand && (initStyle.height = "auto")
|
||||
return initStyle
|
||||
},
|
||||
expandIcon() {
|
||||
return this.expand ? 'iconfont iconDouble_Up' : 'iconfont iconDouble_Down'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$emit("change", this.expandStyle)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.AiPullDown {
|
||||
display: flex;
|
||||
|
||||
.line {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.down-content {
|
||||
cursor: pointer;
|
||||
padding: 0 8px;
|
||||
height: 24px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
border: 1px solid #eee;
|
||||
border-top: 0;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
src/components/AiSearch.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<section class="AiSearch">
|
||||
<slot v-if="$slots.label" name="label"/>
|
||||
<span v-else>{{ label }}</span>
|
||||
<div class="content">
|
||||
<slot/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiSearch",
|
||||
props: {
|
||||
label: {default: ""}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiSearch {
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
background: #F5F5F5;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
border: 1px solid #D0D4DC;
|
||||
border-right: none;
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep( .el-input__inner ){
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
* + * {
|
||||
.el-input__inner {
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
101
src/components/AiSearchBar.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<section>
|
||||
<div ref="AiSearchBarZone" class="AiSearchBar" :class="{bottomBorder}" :style="searchBarStyle">
|
||||
<div class="searchLeftZone">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<div class="searchRightZone" ref="searchRightZone">
|
||||
<slot name="right"/>
|
||||
</div>
|
||||
</div>
|
||||
<ai-pull-down v-if="!isSingleRow" @change="handlePullDown" :height="rightHeight"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiSearchBar",
|
||||
props: {
|
||||
bottomBorder: Boolean,
|
||||
size: {default: "small"}
|
||||
},
|
||||
computed: {
|
||||
isSingleRow() {
|
||||
return this.height <= 55
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
height: 0,
|
||||
rightHeight: 0,
|
||||
searchBarStyle: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePullDown(style) {
|
||||
this.searchBarStyle = style
|
||||
if (style.height == 'auto') {
|
||||
this.searchBarStyle.marginBottom = '16px'
|
||||
} else {
|
||||
this.searchBarStyle.marginBottom = '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.height = this.$refs?.AiSearchBarZone?.offsetHeight
|
||||
this.rightHeight = this.$refs?.searchRightZone?.offsetHeight
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiSearchBar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding-bottom: 12px;
|
||||
|
||||
&.bottomBorder {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
:deep(.searchLeftZone ){
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
:deep(.searchRightZone ){
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
align-self: flex-start;
|
||||
|
||||
.el-input {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
* + button, * + div, * + section {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-input {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.AiPullDown {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
:deep( .searchLeftZone > .el-button), :deep( .searchRightZone > .el-button ){
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
98
src/components/AiSidebar.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="ai-sidebar">
|
||||
<div class="ai-sidebar__tab">
|
||||
<span
|
||||
:class="[currIndex === index ? 'ai-sidebar__tab--active' : '']"
|
||||
v-for="(item, index) in tabTitle"
|
||||
:key="index"
|
||||
@click="changeTab(index)">
|
||||
{{ item }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiSidebar',
|
||||
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
require: true,
|
||||
default: 0
|
||||
},
|
||||
tabTitle: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value (v) {
|
||||
if (v >= 0 && this.currIndex !== v) {
|
||||
this.currIndex = v
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
currIndex: 0
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
if (this.value > 0) {
|
||||
this.currIndex = this.value
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeTab(index) {
|
||||
this.currIndex = index
|
||||
this.$emit('change', index)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-sidebar {
|
||||
position: fixed;
|
||||
margin-left: -20px;
|
||||
font-size: 14px;
|
||||
transform: translateX(-100%);
|
||||
|
||||
.ai-sidebar__tab {
|
||||
min-width: 88px;
|
||||
overflow: hidden;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
font-stretch: normal;
|
||||
letter-spacing: 0;
|
||||
color: #666666;
|
||||
cursor: pointer;
|
||||
border-right: 3px solid #D8DCE3;
|
||||
}
|
||||
|
||||
.ai-sidebar__tab--active {
|
||||
border-right: 3px solid #1FBAD6;
|
||||
color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
400
src/components/AiTable.vue
Normal file
@@ -0,0 +1,400 @@
|
||||
<template>
|
||||
<div class="ai-table" :class="[isShowBorder ? 'ai-table__border' : 'ai-table__noborder']">
|
||||
<el-table :data="tableData"
|
||||
header-cell-class-name="ai-table__header"
|
||||
cell-class-name="ai-table__cell"
|
||||
row-class-name="ai-table__row"
|
||||
:class="{'ai-header__border': isShowBorder}"
|
||||
:ref="refName"
|
||||
:size="tableSize"
|
||||
:stripe="stripe"
|
||||
:tooltip-effect="tooltipEffect"
|
||||
@selection-change="handleSelectionChange"
|
||||
v-on="$listeners"
|
||||
v-bind="$attrs"
|
||||
v-loading="loading">
|
||||
<template v-for="colConfig in colConfigs.filter(e=>!e.hide)">
|
||||
<slot v-if="colConfig.slot && colConfig.slot !== 'options'" :name="colConfig.slot"/>
|
||||
<component
|
||||
:key="colConfig.id"
|
||||
v-else-if="colConfig.component"
|
||||
:is="colConfig.component"
|
||||
:col-config="colConfig">
|
||||
</component>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.dict"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig">
|
||||
<span slot-scope="{row}" :style="{color:colConfig.color||dict.getColor(colConfig.dict, row[colConfig.prop])}">
|
||||
{{ $dict.getLabel(colConfig.dict, row[colConfig.prop]) }}
|
||||
</span>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.openType"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig">
|
||||
<template v-slot="{row}">
|
||||
<ai-open-data :type="colConfig.openType" :openid="row[colConfig.prop]"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.type"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig"
|
||||
:width="colConfig.width || 100"/>
|
||||
<el-table-column v-else v-bind="colConfig" :key="colConfig.id"
|
||||
:show-overflow-tooltip="colConfig['show-overflow-tooltip'] != false">
|
||||
<template slot-scope="scope">
|
||||
<render-slot v-if="colConfig.render" :render="colConfig.render" :row="scope.row" :index="scope.$index"
|
||||
:column="colConfig"/>
|
||||
<span v-else>{{ getValue(colConfig, scope.row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<slot class="table-options" name="options"/>
|
||||
<template #empty>
|
||||
<slot v-if="$scopedSlots.empty" name="empty"/>
|
||||
<AiEmpty v-else class="no-data" style="height:160px;"/>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="pagination newPagination" v-if="isShowPagination">
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="page.current"
|
||||
:total="page.total"
|
||||
:page-size="page.size"
|
||||
v-bind="$attrs"
|
||||
:page-sizes="pageSizes"
|
||||
:layout="layout"
|
||||
:pager-count="page.pagerCount"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleChange">
|
||||
<div class="paginationPre">
|
||||
<el-checkbox v-if="isHasPaginationBtnsSlot" :disabled="!tableData.length" :indeterminate="isIndeterminate"
|
||||
:value="checkAll" @click.native="toggleAllSelection">全选
|
||||
</el-checkbox>
|
||||
<slot name="pagination"/>
|
||||
<div class="pagination-btns">
|
||||
<slot name="paginationBtns"></slot>
|
||||
</div>
|
||||
<div class="paginationPre-total" :style="{marginLeft: isHasPaginationBtnsSlot ? '24px' : 0}">共<label class="color-primary">{{ page.total }}</label>条记录
|
||||
</div>
|
||||
</div>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'dayjs'
|
||||
|
||||
export default {
|
||||
name: 'AiTable',
|
||||
model: {
|
||||
prop: "tableData",
|
||||
event: "change"
|
||||
},
|
||||
props: {
|
||||
colConfigs: {type: Array, required: true},
|
||||
tableData: {type: Array, required: true, default: () => []},
|
||||
current: {default: 1},
|
||||
size: {default: 10},
|
||||
isShowPagination: {type: Boolean, default: true},
|
||||
total: Number,
|
||||
layout: {type: String, default: 'slot,->, prev, pager, next, sizes, jumper'},
|
||||
stripe: {type: Boolean, default: true},
|
||||
loading: {type: Boolean, default: false},
|
||||
tooltipEffect: String,
|
||||
tableSize: String,
|
||||
tableRef: String,
|
||||
pagerCount: {default: 5},
|
||||
pageSizes: {default: () => [10, 20, 50, 100]},
|
||||
pageConfig: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
chooseList: [],
|
||||
defaultPage: {}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
renderSlot: {
|
||||
functional: true,
|
||||
props: {
|
||||
row: Object,
|
||||
render: Function,
|
||||
index: Number,
|
||||
column: {type: Object, default: null},
|
||||
},
|
||||
render: (h, data) => {
|
||||
const params = {
|
||||
row: data.props.row,
|
||||
index: data.props.index
|
||||
}
|
||||
if (data.props.column) {
|
||||
params.column = data.props.column
|
||||
}
|
||||
return data.props.render(h, params)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
refName: v => v.tableRef || `aiTable${new Date().getTime()}`,
|
||||
isShowBorder: v => !!v.$attrs.border || v.$attrs.border === '',
|
||||
isHasPaginationBtnsSlot: v => v.$scopedSlots.paginationBtns,
|
||||
isIndeterminate: v => v.chooseList.length > 0 && v.chooseList.length < v.tableData.length,
|
||||
checkAll: v => v.chooseList.length == v.tableData.length && v.tableData !== 0,
|
||||
page() {
|
||||
return {
|
||||
current: this.current,
|
||||
size: this.size,
|
||||
total: this.total,
|
||||
pagerCount: this.pagerCount,
|
||||
...this.pageConfig
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChange(current) {
|
||||
this.$emit('update:current', current)
|
||||
this.$emit('update:pageConfig', {...this.pageConfig, current})
|
||||
this.$nextTick(() => {
|
||||
this.$emit('getList')
|
||||
})
|
||||
},
|
||||
handleSizeChange(size) {
|
||||
this.$emit('update:size', size)
|
||||
this.$emit('update:pageConfig', {...this.pageConfig, size})
|
||||
this.$nextTick(() => {
|
||||
this.$emit('getList')
|
||||
})
|
||||
},
|
||||
handleSelectionChange(e) {
|
||||
this.chooseList = e
|
||||
this.$emit('handleSelectionChange', e)
|
||||
},
|
||||
getValue(colConfig, row) {
|
||||
if (typeof colConfig.format === 'function') {
|
||||
return colConfig.format.call(this, row[colConfig.prop])
|
||||
}
|
||||
if (colConfig.dateFormat) {
|
||||
return moment(row[colConfig.prop]).format(colConfig.dateFormat)
|
||||
}
|
||||
|
||||
return row[colConfig.prop] === 0 || row[colConfig.prop] ? row[colConfig.prop] : '-'
|
||||
},
|
||||
/**
|
||||
* 表格方法代理
|
||||
*/
|
||||
clearSelection() {
|
||||
this.$refs[this.refName].clearSelection()
|
||||
},
|
||||
toggleRowSelection() {
|
||||
this.$refs[this.refName].toggleRowSelection(...arguments)
|
||||
},
|
||||
toggleAllSelection() {
|
||||
this.$refs[this.refName].toggleAllSelection()
|
||||
},
|
||||
toggleRowExpansion() {
|
||||
this.$refs[this.refName].toggleRowExpansion(...arguments)
|
||||
},
|
||||
setCurrentRow() {
|
||||
this.$refs[this.refName].setCurrentRow(...arguments)
|
||||
},
|
||||
clearSort() {
|
||||
this.$refs[this.refName].clearSort()
|
||||
},
|
||||
clearFilter() {
|
||||
this.$refs[this.refName].clearFilter(...arguments)
|
||||
},
|
||||
doLayout() {
|
||||
this.$refs[this.refName].doLayout()
|
||||
},
|
||||
sort() {
|
||||
this.$refs[this.refName].sort(...arguments)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-table {
|
||||
.color-primary {
|
||||
// color: $primaryColor;
|
||||
}
|
||||
:deep( .ai-header__border .ai-table__header ) {
|
||||
border-bottom: 1px solid #d0d4dc !important;
|
||||
border-right: 1px solid #d0d4dc !important;
|
||||
}
|
||||
|
||||
:deep( .el-table--border ) {
|
||||
border: 1px solid #d0d4dc;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
:deep( .el-table ) {
|
||||
color: #222;
|
||||
|
||||
.caret-wrapper {
|
||||
height: 24px;
|
||||
|
||||
.ascending {
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.descending {
|
||||
bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
thead {
|
||||
color: #555
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .cell ) {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
:deep( .el-table__header ) {
|
||||
th {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
tr {
|
||||
.cell {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
th:first-child {
|
||||
.cell {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .el-table__body ) {
|
||||
tr td:first-child .cell {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .el-table__fixed-right ) {
|
||||
.el-table__body {
|
||||
tr td:first-child .cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .ai-table__header ) {
|
||||
border-bottom: none;
|
||||
background: #F3F4F5;
|
||||
}
|
||||
|
||||
:deep(.el-pager ) {
|
||||
li.active + li {
|
||||
border-left: 1px solid #d0d4dc;
|
||||
}
|
||||
}
|
||||
|
||||
.newPagination {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
padding: 0 40px !important;
|
||||
|
||||
.el-pagination {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.paginationPre {
|
||||
display: flex;
|
||||
height: 28px;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
align-items: center;
|
||||
|
||||
.pagination-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #1FBAD6 !important;
|
||||
|
||||
:deep( span), :deep( div ) {
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
color: #1FBAD6 !important;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.paginationPre-total {
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
|
||||
label {
|
||||
padding: 0 2px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
& > * + * {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
:deep( .el-pagination button), .el-pagination span:not([class*=suffix]) {
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox ) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.el-checkbox__input, .el-checkbox__inner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
min-width: 0 !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
.el-checkbox__label {
|
||||
font-size: 12px;
|
||||
color: #222222;
|
||||
height: auto !important;
|
||||
line-height: 1 !important;
|
||||
padding-left: 3px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-table__noborder {
|
||||
:deep( .el-table td), :deep( .el-table th.is-center ) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.el-table::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep( .el-table--striped .el-table__body tr.el-table__row--striped td ) {
|
||||
background: #F5F6F9;
|
||||
}
|
||||
|
||||
:deep( .el-table__fixed-right::before), :deep( .el-table__fixed::before ) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
140
src/components/AiTitle.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<section class="AiTitle" :class="{ 'bottomBorder': isShowBottomBorder, AiTitleSub: isHasSub}">
|
||||
<i class="iconfont iconBack_Large" v-if="isShowBack" @click="onBackBtnClick"/>
|
||||
<div class="fill">
|
||||
<div class="ailist-title">
|
||||
<div class="ailist-title__left">
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ tips }}</p>
|
||||
</div>
|
||||
<div class="ailist-title__right">
|
||||
<div class="aititle-right__btns">
|
||||
<slot name="rightBtn"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subtitle" v-if="$scopedSlots.sub">
|
||||
<slot name="sub"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiTitle',
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
tips: {
|
||||
type: String
|
||||
},
|
||||
instance: {
|
||||
type: Function
|
||||
},
|
||||
value: {
|
||||
type: String
|
||||
},
|
||||
isShowBack: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isShowBottomBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isHasSub() {
|
||||
return this.$slots.sub
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onBackBtnClick() {
|
||||
this.closePage()
|
||||
this.$emit('onBackClick')
|
||||
this.$emit('back')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AiTitle {
|
||||
display: flex;
|
||||
|
||||
.ailist-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 48px;
|
||||
|
||||
.ailist-title__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
margin-left: 20px;
|
||||
font-size: 14px;
|
||||
color: #FA5555;
|
||||
}
|
||||
|
||||
& > i {
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #222;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.ailist-title__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-button ) {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.AiTitleSub {
|
||||
height: auto;
|
||||
padding: 16px 0;
|
||||
|
||||
.ailist-title {
|
||||
height: auto;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottomBorder {
|
||||
border-bottom: 1px solid #D8DCE3;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
width: 100%;
|
||||
color: #888888;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.iconBack_Large {
|
||||
line-height: 48px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
86
src/components/AiWrapper.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="ai-wrapper">
|
||||
<ai-title class="w100" v-if="title" :title="title"/>
|
||||
<div class="ai-wrapper-content" :class="{border}">
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AiTitle from "./AiTitle";
|
||||
|
||||
export default {
|
||||
name: 'AiWrapper',
|
||||
components: {AiTitle},
|
||||
componentName: 'AiWrapper',
|
||||
|
||||
provide() {
|
||||
return {
|
||||
AiWrapper: this
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
'label-width': {
|
||||
type: String
|
||||
},
|
||||
|
||||
columnsNumber: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
border: Boolean,
|
||||
title: String
|
||||
},
|
||||
|
||||
computed: {
|
||||
autoWidth() {
|
||||
return ((1 / this.columnsNumber) * 100).toFixed(2) + '%'
|
||||
},
|
||||
|
||||
autoLableWidth() {
|
||||
return this.labelWidth
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-wrapper {
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ai-wrapper-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
|
||||
&.border {
|
||||
border-left: 1px solid #d0d4dc;
|
||||
border-top: 1px solid #d0d4dc;
|
||||
|
||||
.ai-info-item {
|
||||
border-bottom: 1px solid #d0d4dc;
|
||||
border-right: 1px solid #d0d4dc;
|
||||
margin-bottom: 0;
|
||||
line-height: 32px;
|
||||
|
||||
.ai-info-item__left {
|
||||
background: rgba(0, 0, 0, .03);
|
||||
padding-right: 16px;
|
||||
border-right: 1px solid #d0d4dc;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
61
src/entry/background.js
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
/**
|
||||
利用chrome的fetch来避免跨域
|
||||
**/
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.type == 'api') {
|
||||
new Promise((resolve, reject) => {
|
||||
let headers = {};
|
||||
|
||||
if (request.needMallId) {
|
||||
headers.Mallid = request.mallId;
|
||||
}
|
||||
headers['Content-Type'] = 'application/json';
|
||||
|
||||
headers.cookie = getCookie();
|
||||
|
||||
fetch(request.url, {
|
||||
'headers': headers,
|
||||
'method': 'POST',
|
||||
'referrerPolicy': 'no-referrer',
|
||||
'credentials': 'include',
|
||||
'body': JSON.stringify(request.data),
|
||||
'mode': 'cors'
|
||||
}).then((res) => {
|
||||
resolve(res.json());
|
||||
});
|
||||
}).then((res) => {
|
||||
sendResponse(res);
|
||||
});
|
||||
} else if (request.type == 'notify') {
|
||||
chrome.notifications.create(
|
||||
"" + Math.random(), {
|
||||
type: "basic",
|
||||
title: "TEMU助手",
|
||||
message: "您的商品【" + request.productName + "】成功加入发货台,请尽快处理",
|
||||
iconUrl: "./icons/48.png"
|
||||
}, null
|
||||
)
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
chrome.action.onClicked.addListener(function () {
|
||||
chrome.tabs.create({
|
||||
url: "./popup.html"
|
||||
}, function (tab) {
|
||||
console.log('tab is:' + tab);
|
||||
});
|
||||
});
|
||||
|
||||
function getCookie() {
|
||||
const url = new URL("https://kuajing.pinduoduo.com/");
|
||||
let cStr = '';
|
||||
chrome.cookies.getAll({domain: url.host}, (cookie) => {
|
||||
cookie.map((c) => {
|
||||
cStr += c.name + '=' + c.value + ';';
|
||||
});
|
||||
});
|
||||
return cStr;
|
||||
}
|
||||
1
src/entry/content.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log('hello world content todo something~')
|
||||
27
src/entry/popup.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import Vue from 'vue'
|
||||
import App from '../view/popup.vue'
|
||||
import store from '../store'
|
||||
import router from '../router'
|
||||
import ElementUI from 'element-ui'
|
||||
import '../assets/css/element-variables.scss'
|
||||
import instance from '../api'
|
||||
import utils from '../utils'
|
||||
import install from '../utils/install'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
Vue.use(ElementUI)
|
||||
Vue.config.productionTip = false
|
||||
|
||||
var relativeTime = require('dayjs/plugin/relativeTime')
|
||||
require('dayjs/locale/zh-cn')
|
||||
dayjs.extend(relativeTime)
|
||||
Vue.prototype.$dayjs = dayjs
|
||||
|
||||
Object.keys(utils).forEach(v => Vue.prototype[`$${v}`] = utils[v])
|
||||
Vue.prototype.$http = instance
|
||||
|
||||
new Vue({
|
||||
store,
|
||||
router,
|
||||
render: (h) => h(App)
|
||||
}).$mount('#app')
|
||||
33
src/manifest.development.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "TEMU助手",
|
||||
"description": "TEMU助手 - 自动化提高生产效率",
|
||||
"version": "0.0.1",
|
||||
"background": {
|
||||
"service_worker": "/background.js"
|
||||
},
|
||||
"icons": {
|
||||
"16": "icons/16.png",
|
||||
"48": "icons/48.png",
|
||||
"128": "icons/128.png"
|
||||
},
|
||||
"action": {
|
||||
},
|
||||
"host_permissions": [
|
||||
"*://*.pinduoduo.com/"
|
||||
],
|
||||
"permissions": [
|
||||
"cookies",
|
||||
"notifications"
|
||||
],
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"<all_urls>"
|
||||
],
|
||||
"js": [
|
||||
"/content.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
33
src/manifest.production.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "TEMU助手",
|
||||
"description": "TEMU助手 - 自动化提高生产效率",
|
||||
"version": "1.0.0",
|
||||
"background": {
|
||||
"service_worker": "/background.js"
|
||||
},
|
||||
"icons": {
|
||||
"16": "icons/16.png",
|
||||
"48": "icons/48.png",
|
||||
"128": "icons/128.png"
|
||||
},
|
||||
"action": {
|
||||
},
|
||||
"host_permissions": [
|
||||
"*://*.pinduoduo.com/"
|
||||
],
|
||||
"permissions": [
|
||||
"cookies",
|
||||
"notifications"
|
||||
],
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"<all_urls>"
|
||||
],
|
||||
"js": [
|
||||
"/content.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
96
src/router/index.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import store from '@/store'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const router = new VueRouter({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
meta: {
|
||||
isAuth: true
|
||||
},
|
||||
redirect: '/Welcome',
|
||||
component: () => import('../view/Home.vue'),
|
||||
children: [
|
||||
{
|
||||
path: 'welcome',
|
||||
name: 'welcome',
|
||||
component: () => import('../view/Welcome.vue')
|
||||
},
|
||||
{
|
||||
path: 'normalSendGoods',
|
||||
name: 'NormalSendGoods',
|
||||
component: () => import('../view/NormalSendGoods.vue')
|
||||
},
|
||||
{
|
||||
path: 'copyProduct',
|
||||
name: 'copyProduct',
|
||||
component: () => import('../view/CopyProduct.vue')
|
||||
},
|
||||
{
|
||||
path: 'saleData',
|
||||
name: 'saleData',
|
||||
component: () => import('../view/ExportSaleData.vue')
|
||||
},
|
||||
// {
|
||||
// path: 'statistics',
|
||||
// name: 'statistics',
|
||||
// component: () => import('../view/Statistics.vue')
|
||||
// },
|
||||
{
|
||||
path: 'learning',
|
||||
name: 'learning',
|
||||
component: () => import('../view/Learning.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
meta: {
|
||||
title: '登录'
|
||||
},
|
||||
component: () => import('../view/login/Login.vue')
|
||||
},
|
||||
{
|
||||
path: '/register',
|
||||
name: 'register',
|
||||
meta: {
|
||||
title: '注册'
|
||||
},
|
||||
component: () => import('../view/login/Register.vue')
|
||||
}
|
||||
],
|
||||
scrollBehavior (to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return { x: 0, y: 0 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const isLogin = !!store.state.token
|
||||
|
||||
if (to.meta.isAuth && !isLogin) {
|
||||
next({
|
||||
path: '/login',
|
||||
query: {
|
||||
redirect: to.fullPath
|
||||
}
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
const originalPush = VueRouter.prototype.push
|
||||
VueRouter.prototype.push = function push(location) {
|
||||
return originalPush.call(this, location).catch(err => err)
|
||||
}
|
||||
|
||||
export default router
|
||||
68
src/store/index.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import preState from 'vuex-persistedstate'
|
||||
import request from '@/api'
|
||||
import router from '@/router'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
token: '',
|
||||
mallId: '',
|
||||
mallName: '',
|
||||
mallList: [],
|
||||
activeDlgShow: false,
|
||||
userInfo: {},
|
||||
routes: [],
|
||||
addRoutes: []
|
||||
},
|
||||
|
||||
mutations: {
|
||||
setToken (state, token) {
|
||||
state.token = token
|
||||
},
|
||||
setMallId (state, mallId) {
|
||||
state.mallId = mallId
|
||||
},
|
||||
setMallName (state, mallName) {
|
||||
state.mallName = mallName
|
||||
},
|
||||
setMallList (state, mallList) {
|
||||
state.mallList = mallList
|
||||
},
|
||||
SignOut (state) {
|
||||
state.token = ''
|
||||
state.userInfo = {}
|
||||
|
||||
setTimeout(() => {
|
||||
router.push('/login')
|
||||
}, 600)
|
||||
},
|
||||
|
||||
setUserInfo (state, userInfo) {
|
||||
state.userInfo = userInfo
|
||||
},
|
||||
setActiveDlgShow (state, flag) {
|
||||
state.activeDlgShow = flag
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
getUserInfo (store) {
|
||||
request.post('/api/malluser/info').then(res => {
|
||||
if (res.code === 0) {
|
||||
store.commit('setUserInfo', res.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
getters: {
|
||||
isLogin: state => {
|
||||
return !!state.token
|
||||
}
|
||||
},
|
||||
|
||||
plugins: [preState()]
|
||||
})
|
||||
14
src/utils/date.js
Normal file
@@ -0,0 +1,14 @@
|
||||
export function timestampToTime(timestamp) {
|
||||
// 时间戳为10位需*1000,时间戳为13位不需乘1000
|
||||
let date = new Date(timestamp);
|
||||
let Y = date.getFullYear() + "-";
|
||||
let M =
|
||||
(date.getMonth() + 1 < 10
|
||||
? "0" + (date.getMonth() + 1)
|
||||
: date.getMonth() + 1) + "-";
|
||||
let D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " ";
|
||||
let h = (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":";
|
||||
let m = (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ":";
|
||||
let s = (date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds());
|
||||
return Y + M + D + h + m + s;
|
||||
}
|
||||
96
src/utils/index.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import request from '../api'
|
||||
|
||||
const dict = {
|
||||
url: "/dictionary/queryValsByCodeList",
|
||||
loading: [],
|
||||
resolves: [],
|
||||
getStorage() {
|
||||
const dicts = JSON.parse(localStorage.getItem('dicts') || null)
|
||||
return dicts?.data || dicts || [];
|
||||
},
|
||||
setUrl(v) {
|
||||
this.url = v
|
||||
},
|
||||
getData(codeList) {
|
||||
codeList = [codeList].flat().filter(Boolean).toString()
|
||||
return request.post(this.url, null, {
|
||||
withoutToken: true, params: {codeList}
|
||||
}).then(res => res?.data && this.setStorage(res.data))
|
||||
},
|
||||
load(...code) {
|
||||
return new Promise(resolve => {
|
||||
this.resolves.push(resolve)
|
||||
if (this.loading.length == 2) {
|
||||
const [timer, codes] = this.loading;
|
||||
clearTimeout(timer)
|
||||
code = Array.from(new Set([codes, code].flat()))
|
||||
}
|
||||
const timer = setTimeout(() => {
|
||||
this.getData(code).then(() => Promise.all(this.resolves.map(e => e())).then(() => this.resolves = []))
|
||||
}, 500)
|
||||
this.loading = [timer, code]
|
||||
})
|
||||
},
|
||||
setStorage(data) {
|
||||
let ds = this.getStorage()
|
||||
data.map(p => {
|
||||
if (ds.some(d => d.key == p.key)) {
|
||||
const index = ds.findIndex(d => d.key == p.key)
|
||||
ds.splice(index, 1, p)
|
||||
} else {
|
||||
ds.push(p)
|
||||
}
|
||||
})
|
||||
localStorage.setItem("dicts", JSON.stringify([ds].flat()))
|
||||
},
|
||||
getDict(key) {
|
||||
let dict = this.getStorage().find(e => e.key == key)
|
||||
!dict && console.warn("字典%s缺少加载...", key)
|
||||
return dict ? dict.values : []
|
||||
},
|
||||
getValue(key, label) {
|
||||
let dict = this.getDict(key)
|
||||
if (dict) {
|
||||
let item = dict.find(v => v.dictName == label)
|
||||
return item ? item.dictValue : label
|
||||
} else return label
|
||||
},
|
||||
getLabel(key, value) {
|
||||
let dict = this.getDict(key)
|
||||
if (dict) {
|
||||
let item = dict.find(v => v.dictValue == value)
|
||||
return item ? item.dictName : value
|
||||
} else return value
|
||||
},
|
||||
getColor(key, value) {
|
||||
let dict = this.getDict(key)
|
||||
if (dict) {
|
||||
let item = dict.find(v => v.dictValue == value)
|
||||
return item ? item.dictColor : value
|
||||
} else return value
|
||||
},
|
||||
}
|
||||
|
||||
const dateUtil = {
|
||||
timestampToTime(timestamp) {
|
||||
// 时间戳为10位需*1000,时间戳为13位不需乘1000
|
||||
let date = new Date(timestamp);
|
||||
let Y = date.getFullYear() + "-";
|
||||
let M =
|
||||
(date.getMonth() + 1 < 10
|
||||
? "0" + (date.getMonth() + 1)
|
||||
: date.getMonth() + 1) + "-";
|
||||
let D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " ";
|
||||
let h = date.getHours() + ":";
|
||||
let m = date.getMinutes() + ":";
|
||||
let s = date.getSeconds();
|
||||
return Y + M + D + h + m + s;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
dict,
|
||||
dateUtil,
|
||||
}
|
||||
|
||||
|
||||
16
src/utils/install.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import Vue from 'vue' // 引入vue
|
||||
|
||||
function changeStr(str){
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
const requireComponent = require.context('../components', false, /\.vue$/)
|
||||
|
||||
requireComponent.keys().forEach(fileName => {
|
||||
const config = requireComponent(fileName)
|
||||
const componentName = changeStr(
|
||||
fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')
|
||||
)
|
||||
|
||||
Vue.component(componentName, config.default || config)
|
||||
})
|
||||
131
src/utils/product.js
Normal file
@@ -0,0 +1,131 @@
|
||||
export function transform(leftData) {
|
||||
let rightData = {};
|
||||
// 分类
|
||||
let leftCategory = leftData.categories;
|
||||
rightData.cat1Id = leftCategory.cat1.catId;
|
||||
rightData.cat2Id = leftCategory.cat2.catId;
|
||||
rightData.cat3Id = leftCategory.cat3.catId;
|
||||
rightData.cat4Id = leftCategory.cat4.catId;
|
||||
rightData.cat5Id = leftCategory.cat5.catId;
|
||||
rightData.cat6Id = leftCategory.cat6.catId;
|
||||
rightData.cat7Id = leftCategory.cat7.catId;
|
||||
rightData.cat8Id = leftCategory.cat8.catId;
|
||||
rightData.cat9Id = leftCategory.cat9.catId;
|
||||
rightData.cat10Id = leftCategory.cat10.catId;
|
||||
|
||||
// 普通属性
|
||||
rightData.productName = leftData.productName;
|
||||
rightData.materialMultiLanguages = leftData.productLocalExtAttr.materialMultiLanguages;
|
||||
rightData.productI18nReqs = leftData.productI18nList;
|
||||
rightData.productPropertyReqs = [];
|
||||
for (let i = 0; i < leftData.productPropertyList.length; i++) {
|
||||
rightData.productPropertyReqs.push({
|
||||
valueUnit: leftData.productPropertyList[i].valueUnit,
|
||||
propValue: leftData.productPropertyList[i].propValue,
|
||||
propName: leftData.productPropertyList[i].propName,
|
||||
refPid: leftData.productPropertyList[i].refPid,
|
||||
vid: leftData.productPropertyList[i].vid,
|
||||
controlType: 1,
|
||||
pid: leftData.productPropertyList[i].pid,
|
||||
templatePid: leftData.productPropertyList[i].templatePid,
|
||||
valueExtendInfo: leftData.productPropertyList[i].valueExtendInfo
|
||||
});
|
||||
}
|
||||
|
||||
// SKC
|
||||
let rightSkc = [];
|
||||
let leftSkc = leftData.productSkcList;
|
||||
|
||||
let productSpecPropertyReqs = [];
|
||||
for(let i = 0; i < leftSkc.length; i++) {
|
||||
let rightSkcItem = {};
|
||||
rightSkcItem.previewImgUrls = leftSkc[i].previewImgUrls;
|
||||
rightSkcItem.productSkcCarouselImageI18nReqs = leftSkc[i].productSkcCarouselImageI18nVOList;
|
||||
rightSkcItem.extCode = leftSkc[i].extCode;
|
||||
rightSkcItem.mainProductSkuSpecReqs = [
|
||||
{
|
||||
"parentSpecId": 0,
|
||||
"parentSpecName": "",
|
||||
"specId": 0,
|
||||
"specName": ""
|
||||
}
|
||||
];
|
||||
rightSkcItem.productSkuReqs = [];
|
||||
for(let j = 0; j < leftSkc[i].productSkuList.length; j++) {
|
||||
let leftSkuItem = leftSkc[i].productSkuList[j];
|
||||
let rightSkuItem = {};
|
||||
|
||||
rightSkuItem.thumbUrl = leftSkuItem.thumbUrl;
|
||||
rightSkuItem.productSkuThumbUrlI18nReqs = leftSkuItem.productSkuThumbUrlI18nVOList;
|
||||
rightSkuItem.extCode = leftSkuItem.extCode;
|
||||
rightSkuItem.supplierPrice = leftSkuItem.supplierPrice;
|
||||
rightSkuItem.currencyType = leftSkuItem.currencyType;
|
||||
rightSkuItem.productSkuSpecReqs = leftSkuItem.productSkuSpecList;
|
||||
productSpecPropertyReqs.push({
|
||||
"parentSpecId": leftSkuItem.productSkuSpecList[0].parentSpecId,
|
||||
"parentSpecName": leftSkuItem.productSkuSpecList[0].parentSpecName,
|
||||
"specId": leftSkuItem.productSkuSpecList[0].specId,
|
||||
"specName": leftSkuItem.productSkuSpecList[0].specName,
|
||||
"refPid": 0,
|
||||
"pid": 0,
|
||||
"templatePid": 0,
|
||||
"propName": leftSkuItem.productSkuSpecList[0].specName,
|
||||
"vid": 0,
|
||||
"propValue": leftSkuItem.productSkuSpecList[0].specName,
|
||||
"valueUnit": "",
|
||||
"valueGroupId": 0,
|
||||
"valueGroupName": "",
|
||||
"valueExtendInfo": ""
|
||||
});
|
||||
rightSkuItem.productSkuId = 0;
|
||||
rightSkuItem.productSkuWhExtAttrReq = {
|
||||
"productSkuVolumeReq": leftSkuItem.productSkuWhExtAttr.productSkuVolume,
|
||||
"productSkuWeightReq": leftSkuItem.productSkuWhExtAttr.productSkuWeight,
|
||||
"productSkuBarCodeReqs": leftSkuItem.productSkuWhExtAttr.productSkuBarCodes,
|
||||
"productSkuSensitiveAttrReq": {
|
||||
"isSensitive": leftSkuItem.productSkuWhExtAttr.productSkuSensitiveAttr.isSensitive,
|
||||
"sensitiveList": leftSkuItem.productSkuWhExtAttr.productSkuSensitiveAttr.sensitiveList},
|
||||
"productSkuSensitiveLimitReq": leftSkuItem.productSkuWhExtAttr.productSkuSensitiveLimit,
|
||||
};
|
||||
rightSkuItem.currencyType = leftSkuItem.currencyType;
|
||||
|
||||
rightSkcItem.productSkuReqs.push(rightSkuItem);
|
||||
}
|
||||
rightSkcItem.productSkcId = 0;
|
||||
|
||||
rightSkc.push(rightSkcItem);
|
||||
}
|
||||
rightData.productSkcReqs = rightSkc;
|
||||
|
||||
// Spec
|
||||
rightData.productSpecPropertyReqs = productSpecPropertyReqs;
|
||||
rightData.carouselImageUrls = leftData.carouselImageUrls;
|
||||
rightData.carouselImageI18nReqs = leftData.carouselImageI18nVOList;
|
||||
rightData.materialImgUrl = leftData.materialImgUrl;
|
||||
rightData.goodsLayerDecorationReqs = leftData.goodsLayerDecorationVOList;
|
||||
rightData.sizeTemplateIds = !leftData.sizeTemplateIds ? []: leftData.sizeTemplateIds;
|
||||
rightData.showSizeTemplateIds = !leftData.showSizeTemplateIds ? []: leftData.showSizeTemplateIds;
|
||||
rightData.goodsModelReqs = !leftData.goodsModelList ? []: leftData.goodsModelList;
|
||||
rightData.productWhExtAttrReq = {
|
||||
outerGoodsUrl: leftData.productWhExtAttr.outerGoodsUrl,
|
||||
productOrigin: {
|
||||
countryShortName: leftData.productWhExtAttr.productOrigin.countryShortName
|
||||
}
|
||||
};
|
||||
rightData.productCarouseVideoReqList = leftData.carouseVideoVOList;
|
||||
rightData.goodsAdvantageLabelTypes = leftData.goodsAdvantageLabelVOList;
|
||||
rightData.productDetailVideoReqList = leftData.detailVideoVOList;
|
||||
rightData.productOuterPackageImageReqs = [];
|
||||
for (let i = 0;i < leftData.outerPackageImages.length; i++) {
|
||||
rightData.productOuterPackageImageReqs.push({
|
||||
imageUrl: leftData.outerPackageImages[i].imageUrl
|
||||
})
|
||||
}
|
||||
rightData.productOuterPackageReq = leftData.productWhExtAttr.productOuterPackage;
|
||||
rightData.sensitiveTransNormalFileReqs = leftData.productWhExtAttr.sensitiveTransNormalFiles;
|
||||
rightData.productGuideFileI18nReqs = leftData.productGuideFileI18nList;
|
||||
rightData.productSaleExtAttrReq = {};
|
||||
rightData.productDraftId = "";
|
||||
|
||||
return JSON.stringify(rightData);
|
||||
}
|
||||
260
src/view/CopyProduct.vue
Normal file
@@ -0,0 +1,260 @@
|
||||
<template>
|
||||
<div>
|
||||
<ai-list class="list">
|
||||
<ai-title
|
||||
slot="title"
|
||||
title="商品模板"
|
||||
tips="请先在当前浏览器登录“拼多多跨境卖家中心”,期间保持登录状态"
|
||||
isShowBottomBorder>
|
||||
</ai-title>
|
||||
<template slot="content">
|
||||
<div class="content">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button type="button" :icon="'el-icon-delete'" :class="'el-button el-button--primary'" @click="remove()">删除</el-button>
|
||||
<el-button v-if="$store.state.mallName" type="button" :class="'el-button el-button--primary'" @click="toAddTemplate()">从“{{$store.state.mallName}}”添加商品模板</el-button>
|
||||
<el-button v-if="$store.state.mallName" type="button" :class="'el-button el-button--primary'" @click="addToDraft()">将商品模板添加到“{{$store.state.mallName}}”草稿箱</el-button>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-button size="small" circle icon="el-icon-refresh-right" @click="getList()"></el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
style="margin-top: 8px;"
|
||||
:current.sync="search.current" :size.sync="search.size"
|
||||
@selection-change="handleSelectionChange"
|
||||
@getList="getList">
|
||||
</ai-table>
|
||||
</div>
|
||||
</template>
|
||||
</ai-list>
|
||||
|
||||
<el-dialog
|
||||
title="商品列表"
|
||||
:visible.sync="dlgShow"
|
||||
:close-on-click-modal="false"
|
||||
width="70%"
|
||||
:before-close="handleClose">
|
||||
<ai-table
|
||||
:tableData="productTableData"
|
||||
:col-configs="productColConfigs"
|
||||
:total="productPage.total"
|
||||
:current.sync="productPage.page" :size.sync="productPage.pageSize"
|
||||
style="margin-top: 8px;"
|
||||
@selection-change="productHandleSelectionChange"
|
||||
@getList="getProductList">
|
||||
</ai-table>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dlgShow = false">取 消</el-button>
|
||||
<el-button type="primary" @click="saveProduct">添加到模板</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {sendChromeAPIMessage} from '@/api/chromeApi'
|
||||
import {timestampToTime} from '@/utils/date'
|
||||
import {transform} from '@/utils/product'
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
export default {
|
||||
name: 'List',
|
||||
|
||||
data () {
|
||||
return {
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
productName: '',
|
||||
mallName: '',
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
},
|
||||
colConfigs: [
|
||||
{ type: "selection", width: '70px', align: 'left', fixed: 'left'},
|
||||
{ prop: 'productSpu', label: 'SPU', align: 'left' },
|
||||
{ prop: 'productName', label: '商品名称', align: 'left' },
|
||||
{ prop: 'mallName', label: '来源店铺', align: 'left'},
|
||||
{ prop: 'createTime', label: '添加时间', width: '180px', fixed: 'right'}
|
||||
],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
ids: [],
|
||||
|
||||
dlgShow: false,
|
||||
productTableData: [],
|
||||
productPage: {page: 1, pageSize: 10, total: 0},
|
||||
productColConfigs: [
|
||||
{ type: "selection", width: '70px', align: 'left', fixed: 'left'},
|
||||
{ prop: 'productSpu', label: 'SPU ID', align: 'left' },
|
||||
{ prop: 'productSkc', label: 'SKC ID', align: 'left' },
|
||||
{ prop: 'productName', label: '商品名称', align: 'left' },
|
||||
{ prop: 'createTime', label: '创建时间', align: 'left' }
|
||||
],
|
||||
productIds: [],
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.getList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList () {
|
||||
this.$http.post('/api/product/myPage',null,{
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
})
|
||||
},
|
||||
remove () {
|
||||
if (this.ids.length <= 0) {
|
||||
alert('请选择要删除的商品');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定要删除?', '温馨提示', {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$http.post('/api/product/delByIds',this.ids
|
||||
).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.ids = [];
|
||||
val.forEach(e => {
|
||||
this.ids.push(e.id);
|
||||
});
|
||||
},
|
||||
|
||||
// 添加模板
|
||||
handleClose() {
|
||||
this.productTableData = []
|
||||
this.productPage = {page: 1, pageSize: 10, total: 0}
|
||||
this.dlgShow = false
|
||||
},
|
||||
toAddTemplate() {
|
||||
this.$http.post('/api/malluser/info').then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$store.commit('setUserInfo', res.data)
|
||||
if (res.data.flag != 1) {
|
||||
Message.error('您的账号未激活或已失效,请激活后使用')
|
||||
this.$store.commit('setActiveDlgShow', true)
|
||||
return;
|
||||
}
|
||||
this.dlgShow = true
|
||||
this.getProductList()
|
||||
}
|
||||
})
|
||||
},
|
||||
getProductList() {
|
||||
sendChromeAPIMessage({
|
||||
url: 'bg-visage-mms/product/skc/pageQuery',
|
||||
needMallId: true,
|
||||
data: {
|
||||
...this.productPage
|
||||
}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
this.productPage.total = res.result.total
|
||||
this.productTableData = res.result.pageItems.map((item) => {
|
||||
console.log(item.createdAt)
|
||||
return {
|
||||
productSpu: item.productId,
|
||||
productSkc: item.productSkcId,
|
||||
productName: item.productName,
|
||||
createTime: timestampToTime(item.createdAt)
|
||||
};
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
productHandleSelectionChange(val) {
|
||||
this.productIds = [];
|
||||
val.forEach(e => {
|
||||
this.productIds.push(e.productSpu);
|
||||
});
|
||||
},
|
||||
saveProduct() {
|
||||
if (this.productIds.length <= 0) {
|
||||
alert('请选择商品');
|
||||
return;
|
||||
}
|
||||
this.productIds.map((productSpu, index) => {
|
||||
sendChromeAPIMessage({
|
||||
url: 'bg-visage-mms/product/query',
|
||||
needMallId: true,
|
||||
data: {
|
||||
productEditTaskUid: '',
|
||||
productId: productSpu
|
||||
}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
let content = transform(res.result)
|
||||
this.$http.post('/api/product/add', {
|
||||
mallName: this.$store.state.mallName,
|
||||
productSpu: res.result.productId,
|
||||
productName: res.result.productName,
|
||||
content: content
|
||||
}).then(res1 => {
|
||||
if (res1.code == 0) {
|
||||
Message.success("商品【" + res.result.productName + "】成功添加为商品模板")
|
||||
if (index == this.productIds.length - 1) {
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
addToDraft() {
|
||||
if (this.ids.length <= 0) {
|
||||
alert('请选择商品模板');
|
||||
return;
|
||||
}
|
||||
this.ids.map((id, index) => {
|
||||
let product = this.tableData.filter((item) => {
|
||||
return item.id == id
|
||||
})
|
||||
setTimeout(() => {
|
||||
sendChromeAPIMessage({
|
||||
url: 'bg-visage-mms/product/draft/add',
|
||||
needMallId: true,
|
||||
data: {
|
||||
catId: 26117
|
||||
}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
let draftId = res.result.productDraftId
|
||||
let content = JSON.parse(product[0].content)
|
||||
content.productDraftId = draftId
|
||||
sendChromeAPIMessage({
|
||||
url: 'bg-visage-mms/product/draft/save',
|
||||
needMallId: true,
|
||||
data: {
|
||||
...content
|
||||
}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
Message.success("商品【" + product[0].productName + "】成功添加到草稿箱")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}, 1000*index)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
206
src/view/ExportSaleData.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
|
||||
<ai-list class="list">
|
||||
<ai-title
|
||||
slot="title"
|
||||
title="销售数据"
|
||||
isShowBottomBorder>
|
||||
</ai-title>
|
||||
<template slot="content">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
</template>
|
||||
<template #right>
|
||||
<json-excel
|
||||
:data="list"
|
||||
:fields="jsonFields"
|
||||
:before-generate = "startDownload"
|
||||
name="销售数据.xls"
|
||||
worksheet="销售统计">
|
||||
<el-button size="small">导出销售数据</el-button>
|
||||
</json-excel>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:isShowPagination="false"
|
||||
:tableData="list"
|
||||
:col-configs="colConfigs"
|
||||
:total="list.length"
|
||||
style="margin-top: 8px;"
|
||||
@getList="() => {}">
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {sendChromeAPIMessage} from '@/api/chromeApi'
|
||||
import JsonExcel from 'vue-json-excel'
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
export default {
|
||||
name: 'ExportSaleData',
|
||||
|
||||
data () {
|
||||
return {
|
||||
list: [],
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
colConfigs: [
|
||||
{
|
||||
prop: 'productName',
|
||||
label: '商品名称',
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
prop: 'productId',
|
||||
label: 'SPU',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
prop: 'productSkcId',
|
||||
label: 'SKC',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
prop: 'className',
|
||||
label: 'SKU属性',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
prop: 'supplierPrice',
|
||||
label: '申报价格(CNY)',
|
||||
align: 'center',
|
||||
format: v => v / 100
|
||||
},
|
||||
{
|
||||
prop: 'isVerifyPrice',
|
||||
label: '开款核价状态',
|
||||
align: 'center',
|
||||
format: v => v ? '核价通过': '核价未通过 / 无法备货'
|
||||
},
|
||||
],
|
||||
jsonFields: {
|
||||
"商品名称": "productName",
|
||||
"SPU": "productId",
|
||||
"SKC": "productSkcId",
|
||||
"SKU属性": "className",
|
||||
"申报价格(CNY)": {
|
||||
"field": "supplierPrice",
|
||||
callback: (value) => {
|
||||
return value /100;
|
||||
}
|
||||
},
|
||||
"SKU货号": "skuExtCode",
|
||||
"开款核价状态": {
|
||||
"field": "isVerifyPrice",
|
||||
callback: (value) => {
|
||||
return value ? '核价通过': '核价未通过 / 无法备货';
|
||||
}
|
||||
},
|
||||
"缺货数量": "lackQuantity",
|
||||
"建议备货量": "adviceQuantity",
|
||||
"可售天数": "availableSaleDays",
|
||||
"库存可售天数": "availableSaleDaysFromInventory",
|
||||
"仓内库存可售天数": "warehouseAvailableSaleDays",
|
||||
"近7日用户加购数量": "inCartNumber7d",
|
||||
"用户累计加购数量": "inCardNumber",
|
||||
"已订阅待提醒到货": "nomsgSubsCntCntSth",
|
||||
"销售数据 - 今日": "todaySaleVolume",
|
||||
"销售数据 - 近7日": "lastSevenDaysSaleVolume",
|
||||
"销售数据 - 近30天": "lastThirtyDaysSaleVolume",
|
||||
"库存数据 - 仓内可用库存": "inventoryNumInfo.warehouseInventoryNum",
|
||||
"库存数据 - 仓内暂不可用库存": "inventoryNumInfo.unavailableWarehouseInventoryNum",
|
||||
"库存数据 - 已发货库存": "inventoryNumInfo.waitReceiveNum",
|
||||
"库存数据 - 已下单待发货库存": "inventoryNumInfo.waitDeliveryInventoryNum",
|
||||
"库存数据 - 待审核备货库存": "inventoryNumInfo.waitApproveInventoryNum",
|
||||
"VMI备货单数 - 待发货": "vmiOrderInfo.waitDeliveryNum",
|
||||
"VMI备货单数 - 在途单数": "vmiOrderInfo.transportationNum",
|
||||
"VMI备货单数 - 发货延迟": "vmiOrderInfo.deliveryDelayNum",
|
||||
"VMI备货单数 - 到货延迟": "vmiOrderInfo.arrivalDelayNum",
|
||||
"非VMI备货单数 - 待发货": "notVmiOrderInfo.waitDeliveryNum",
|
||||
"非VMI备货单数 - 在途单数": "notVmiOrderInfo.transportationNum",
|
||||
"非VMI备货单数 - 发货延迟": "notVmiOrderInfo.deliveryDelayNum",
|
||||
"非VMI备货单数 - 到货延迟": "notVmiOrderInfo.arrivalDelayNum",
|
||||
"备货逻辑": "purchaseConfig"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
JsonExcel
|
||||
},
|
||||
|
||||
created () {
|
||||
this.$http.post('/api/malluser/info').then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$store.commit('setUserInfo', res.data)
|
||||
if (res.data.flag != 1) {
|
||||
Message.error('您的账号未激活或已失效,请激活后使用')
|
||||
this.$store.commit('setActiveDlgShow', true)
|
||||
return;
|
||||
} else {
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList () {
|
||||
sendChromeAPIMessage({
|
||||
url: 'marvel-mms/cn/api/kiana/venom/sales/management/list',
|
||||
needMallId: true,
|
||||
data: {
|
||||
pageNo: this.currentPage,
|
||||
pageSize: this.pageSize,
|
||||
isLack: 0,
|
||||
priceAdjustRecentDays: 7
|
||||
}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
// this.list = res.result.subOrderList
|
||||
|
||||
for(let i = 0;i < res.result.subOrderList.length; i++) {
|
||||
let item = res.result.subOrderList[i];
|
||||
let data = {};
|
||||
data.productName = item.productName;
|
||||
data.productId = item.productId;
|
||||
data.productSkcId = item.productSkcId;
|
||||
data.purchaseConfig = item.purchaseConfig;
|
||||
|
||||
for(let j = 0;j < item.skuQuantityDetailList.length; j++) {
|
||||
data = {...data, ...item.skuQuantityDetailList[j]}
|
||||
this.list.push(data);
|
||||
}
|
||||
}
|
||||
if (this.pageSize == res.result.subOrderList.length) {
|
||||
this.currentPage ++
|
||||
this.getList()
|
||||
} else {
|
||||
Message.success('销售数据加载完成,可进行导出')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.log(this.list)
|
||||
});
|
||||
},
|
||||
startDownload() {
|
||||
this.$http.post('/api/malluser/info').then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$store.commit('setUserInfo', res.data)
|
||||
if (res.data.flag != 1) {
|
||||
Message.error('您的账号未激活或已失效,请激活后使用')
|
||||
this.$store.commit('setActiveDlgShow', true)
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
387
src/view/Home.vue
Normal file
@@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<div class="admin">
|
||||
<div class="admin-top">
|
||||
<div class="logo">
|
||||
<!-- <img src="../../assets/images/logo.png" /> -->
|
||||
<div>
|
||||
<h1>TEMU助手</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-right">
|
||||
<div class="left" @click="toActive">
|
||||
<span>{{ $store.state.userInfo.name + "(" + $store.state.userInfo.phone + ")" }}</span>
|
||||
<div :style="{marginLeft: '10px', color: $store.state.userInfo.flag == 1? 'green': 'red'}">({{ getStateInfo }})</div>
|
||||
</div>
|
||||
<el-dropdown @command="onMallChange" v-if="mallName">
|
||||
<div class="userinfo">
|
||||
<span>{{ mallName }}</span>
|
||||
<img src="../assets/images/bottom.png" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="(item, index) in mallList"
|
||||
:command="item"
|
||||
:key="index">
|
||||
{{ item.mallName }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-main">
|
||||
<el-scrollbar class="left">
|
||||
<el-menu
|
||||
:default-active="activePath"
|
||||
:collapse-transition="true"
|
||||
unique-opened
|
||||
background-color="#545c64"
|
||||
router
|
||||
text-color="#fff"
|
||||
:collapse="isCollapse">
|
||||
<el-menu-item index="/welcome">
|
||||
<i class="el-icon-monitor"></i>
|
||||
<span slot="title">工作台</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/normalSendGoods">
|
||||
<i class="el-icon-shopping-cart-2"></i>
|
||||
<span slot="title">普通备货单</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/copyProduct">
|
||||
<i class="el-icon-document-copy"></i>
|
||||
<span slot="title">商品复制</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/saleData">
|
||||
<i class="el-icon-s-data"></i>
|
||||
<span slot="title">销售数据</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/learning">
|
||||
<i class="el-icon-s-data"></i>
|
||||
<span slot="title">学习园地</span>
|
||||
</el-menu-item>
|
||||
<!-- <el-menu-item index="/statistics">
|
||||
<i class="el-icon-s-data"></i>
|
||||
<span slot="title">数据统计</span>
|
||||
</el-menu-item> -->
|
||||
</el-menu>
|
||||
<div style="position: absolute; bottom: 20px; padding-left: 20px; ">
|
||||
<div style="cursor: pointer; font-size: 16px;" @click="$store.commit('SignOut', true)">
|
||||
<i class="el-icon-back" style="color: white; margin-right: 5px;"></i>
|
||||
<span style="color: white">退出</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="container">
|
||||
<transition name="fade-transform" mode="out-in">
|
||||
<keep-alive include="NormalSendGoods">
|
||||
<router-view class="container-app"></router-view>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="用户激活"
|
||||
:visible="$store.state.activeDlgShow"
|
||||
:close-on-click-modal="false"
|
||||
width="30%"
|
||||
:before-close="handleClose">
|
||||
<el-form :model="form" label-position="top" ref="form" label-width="100px" class="form">
|
||||
<el-form-item
|
||||
prop="mallName"
|
||||
label="当前绑定账号"
|
||||
:rules="[{ required: true, message: '请先登录拼多多跨境卖家中心', trigger: 'blur' }]">
|
||||
<el-input readonly placeholder="请先登录拼多多跨境卖家中心" v-model="form.mallName"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="mallId"
|
||||
v-show="false"
|
||||
:rules="[{ message: '请输入商城ID', trigger: 'blur' }]">
|
||||
<el-input placeholder="请输入商城ID" v-model="form.mallId"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="code"
|
||||
label="激活码"
|
||||
:rules="[{ required: true, message: '请输入激活码', trigger: 'blur' }]">
|
||||
<el-input placeholder="请输入激活码" v-model="form.code"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="$store.commit('setActiveDlgShow', false)">取 消</el-button>
|
||||
<el-button type="primary" @click="active">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
isCollapse: false,
|
||||
activePath: '/home',
|
||||
form: {
|
||||
mallId: this.$store.state.mallId,
|
||||
mallName: this.$store.state.mallName,
|
||||
code: ''
|
||||
},
|
||||
vipType: ["体验会员","月会员","半年会员","年会员","年会员多店通用"]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getStateInfo() {
|
||||
if (this.$store.state.userInfo.flag == 0) {
|
||||
return '未激活';
|
||||
} else if (this.$store.state.userInfo.flag == 1) {
|
||||
return this.vipType[this.$store.state.userInfo.type] + '/有效期至' + this.$store.state.userInfo.expireTime;
|
||||
} else {
|
||||
return '已过期';
|
||||
}
|
||||
},
|
||||
|
||||
...mapState(['mallName', 'mallList'])
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route (v) {
|
||||
this.activePath = v.fullPath
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.activePath = this.$route.fullPath
|
||||
},
|
||||
|
||||
methods: {
|
||||
onMallChange (e) {
|
||||
this.$store.commit('setMallName', e.mallName)
|
||||
this.$store.commit('setMallId', e.mallId)
|
||||
location.reload()
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.form.mallId = "";
|
||||
this.form.mallName = "";
|
||||
this.form.code = "";
|
||||
this.$store.commit('setActiveDlgShow', false)
|
||||
},
|
||||
toActive() {
|
||||
this.$store.commit('setActiveDlgShow', true)
|
||||
},
|
||||
getMessage(type) {
|
||||
return `你使用的是“${this.vipType[type]}”兑换券,确定兑换?`;
|
||||
},
|
||||
active() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$http.post(`/api/coupon/getDetail`, null, {
|
||||
params: {
|
||||
code: this.form.code
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
let msg = this.getMessage(res.data.type);
|
||||
this.$confirm(msg, '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
}).then(() => {
|
||||
this.$http.post(`/api/order/upgradeByCode`, null, {
|
||||
params: {
|
||||
...this.form
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('激活成功')
|
||||
this.$store.dispatch('getUserInfo')
|
||||
this.$store.commit('setActiveDlgShow', false)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.admin {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: #f4f4f4;
|
||||
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all .4s;
|
||||
}
|
||||
|
||||
.fade-transform-enter {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.admin-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: calc(100% - 64px);
|
||||
overflow: hidden;
|
||||
border-top: 1px solid rgba(0,0,0,0.15);
|
||||
|
||||
.container {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.container-app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 246px;
|
||||
height: 100%;
|
||||
background: #545c64;
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 64px;
|
||||
padding: 0 24px 0 36px;
|
||||
// box-shadow: 0px 1px 0px 0px rgba(128, 128, 128, 0.1);
|
||||
background: #fff;
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
line-height: 25px;
|
||||
color: #1F2635;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 1px;
|
||||
line-height: 17px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #A5A9BC;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-middle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
|
||||
span {
|
||||
position: relative;
|
||||
margin-right: 60px;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
font-size: 16px;
|
||||
color: #1F2635;
|
||||
cursor: pointer;
|
||||
transition: all ease 0.5s;
|
||||
|
||||
&.active::after {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 9px;
|
||||
z-index: 1;
|
||||
width: 52px;
|
||||
height: 4px;
|
||||
background: #00A971;
|
||||
transform: translateX(-50%);
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
&.active, &:hover {
|
||||
color: #1FBAD6;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all ease 0.3s;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 56px;
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
transition: all ease 0.3s;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-right: 8px;
|
||||
color: #1F2635;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
97
src/view/Learning.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<ai-list class="Learning">
|
||||
<ai-title
|
||||
slot="title"
|
||||
title="学习园地"
|
||||
isShowBottomBorder>
|
||||
</ai-title>
|
||||
<template slot="content">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-radio-group v-model="search.categoryId">
|
||||
<el-radio-button label="">全部</el-radio-button>
|
||||
<el-radio-button label="学院">我的收藏</el-radio-button>
|
||||
<el-radio-button :label="item.id" :key="item.id" v-for="item in cateList">{{ item.name }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<template #right>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size"
|
||||
style="margin-top: 8px;"
|
||||
@getList="getList">
|
||||
<el-table-column slot="options" label="操作" align="center" fixed="right" width="90px">
|
||||
<template v-slot="{row}">
|
||||
<div class="table-options">
|
||||
<el-button type="text">收藏</el-button>
|
||||
<el-button type="text" @click="toDetail(row.url)">详情</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Learning',
|
||||
|
||||
data () {
|
||||
return {
|
||||
colConfigs: [
|
||||
{ prop: 'title', label: '标题', align: 'left' },
|
||||
{ prop: 'subPurchaseOrderSn', label: '是否收藏', align: 'center' },
|
||||
],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
categoryId: ''
|
||||
},
|
||||
cateList: []
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.getCateList()
|
||||
this.getList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
toDetail (url) {
|
||||
window.open(url)
|
||||
},
|
||||
|
||||
getCateList () {
|
||||
this.$http.post('/api/learningCategory/page?size=50').then(res => {
|
||||
if (res.code === 0) {
|
||||
this.cateList = res.data.records
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getList () {
|
||||
this.$http.post('/api/learning/pluginPage', null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
336
src/view/NormalSendGoods.vue
Normal file
@@ -0,0 +1,336 @@
|
||||
<template>
|
||||
<div>
|
||||
<ai-list class="list">
|
||||
<ai-title
|
||||
slot="title"
|
||||
title="普通备货单"
|
||||
tips="请先在当前浏览器登录“拼多多跨境卖家中心”,期间保持登录状态"
|
||||
isShowBottomBorder>
|
||||
</ai-title>
|
||||
<template slot="content">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button type="button" :icon="isBegin? 'el-icon-loading': ''" :class="isBegin ? 'el-button el-button--danger': 'el-button el-button--primary'" @click="beginRob()">{{isBegin ? '结束抢仓': '开始抢仓'}}</el-button>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-button size="small" circle icon="el-icon-refresh-right" @click="getList()"></el-button>
|
||||
</template>
|
||||
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
:isShowPagination="false"
|
||||
style="margin-top: 8px;"
|
||||
@selection-change="handleSelectionChange"
|
||||
@getList="getList">
|
||||
<el-table-column slot="productName" width="400px" label="商品信息" show-overflow-tooltip align="left">
|
||||
<template slot-scope="scope">
|
||||
<div>
|
||||
<div class="order-manage_productInfo__1pD83">
|
||||
<img :src="scope.row.productSkcPicture">
|
||||
<div>备货母单号: {{ scope.row.originalPurchaseOrderSn }}</div>
|
||||
<div>{{ scope.row.productName }}</div>
|
||||
<div>SKC: {{ scope.row.productSkcId }}</div>
|
||||
<div>货号: {{ scope.row.productSn }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="robInfo" label="抢仓信息" show-overflow-tooltip align="left">
|
||||
<template slot-scope="scope">
|
||||
<div data-testid="beast-core-box" style="margin-right: 2px;">
|
||||
已抢仓 <div style="color: red; display: inline;">{{ scope.row.robTotal }}</div> 次
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="status" label="状态" show-overflow-tooltip align="left">
|
||||
<template slot-scope="scope">
|
||||
<div data-testid="beast-core-box" class="outerWrapper-1-3-1 outerWrapper dot-module__dot___M-RuH " style="margin-right: 2px;">
|
||||
<div class="dot-module__circle___2l2UV" style="background-color: var(--pc-dot-warn-bg-color,#ff6800);"></div>待发货
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="className" label="SKU信息" width="400px" show-overflow-tooltip align="left">
|
||||
<template slot-scope="scope">
|
||||
<div class="order-manage_skuInfo__FW-Nd" v-for="(item, index) in scope.row.detailList">
|
||||
<div>
|
||||
<div data-testid="beast-core-box" class="outerWrapper-1-3-1 outerWrapper-d18-1-3-20 index-module__image-preview___2fiZX">
|
||||
<div class="index-module__img___p3B1N" :style="getStyle(item.thumbUrlList[0])"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-manage_contentInfo__1Cjd6" style="max-width: 150px;">
|
||||
<div class="order-manage_title__1VTO5">属性:{{ item.className }},下单数量:{{ item.purchaseQuantity }}</div>
|
||||
<div>SKU ID: {{ item.productSkuId }}</div>
|
||||
<div class="order-manage_productSku__XP_ke">SKU 货号: {{ item.extCode }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {sendChromeAPIMessage, sendChromeNotification} from '@/api/chromeApi'
|
||||
import { Message } from 'element-ui'
|
||||
export default {
|
||||
name: 'NormalSendGoods',
|
||||
|
||||
data () {
|
||||
return {
|
||||
colConfigs: [
|
||||
{ type: "selection", width: '70px', align: 'left', fixed: 'left'},
|
||||
{ prop: 'subPurchaseOrderSn', label: '备货单号', width: '150px', align: 'left', fixed: 'left' },
|
||||
{ slot: 'productName', fixed: 'left'},
|
||||
{ slot: 'className'},
|
||||
{ slot: 'robInfo'},
|
||||
{ slot: 'status' }
|
||||
],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
ids: [],
|
||||
isBegin: false,
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.getList()
|
||||
},
|
||||
|
||||
activated () {
|
||||
this.getList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList () {
|
||||
sendChromeAPIMessage({
|
||||
url: 'oms/bg/venom/api/supplier/purchase/manager/querySubOrderList',
|
||||
needMallId: true,
|
||||
data: {
|
||||
"pageNo": 1,
|
||||
"pageSize": 100,
|
||||
"urgencyType": 0,
|
||||
"isCustomGoods": false,
|
||||
"statusList": [
|
||||
1
|
||||
]
|
||||
}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
res.result.subOrderForSupplierList = res.result.subOrderForSupplierList.filter((item) => {
|
||||
return item.isCanJoinDeliverPlatform;
|
||||
})
|
||||
this.tableData = res.result.subOrderForSupplierList.map((item) => {
|
||||
return {
|
||||
robTotal: 0,
|
||||
subPurchaseOrderSn: item.subPurchaseOrderSn,
|
||||
originalPurchaseOrderSn: item.originalPurchaseOrderSn,
|
||||
productName: item.productName,
|
||||
productSn: item.productSn,
|
||||
productSkcId: item.productSkcId,
|
||||
productSkcPicture: item.productSkcPicture,
|
||||
status: item.status,
|
||||
detailList: item.skuQuantityDetailList
|
||||
};
|
||||
})
|
||||
} else {
|
||||
Message.error("【拼多多】" + res.error_msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.ids = [];
|
||||
val.forEach(e => {
|
||||
this.ids.push(e.subPurchaseOrderSn);
|
||||
});
|
||||
},
|
||||
getStyle(url) {
|
||||
return "background-image: url(" + url + "); width: 72px; height: 72px; cursor: pointer; border-radius: 6px; border: 1px solid rgba(0, 0, 0, 0.14);";
|
||||
},
|
||||
beginRob() {
|
||||
if (this.isBegin) {
|
||||
this.isBegin = false;
|
||||
for (let i = 0;i < this.timer.length; i++) {
|
||||
clearInterval(this.timer[i].timer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.$http.post('/api/malluser/info').then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$store.commit('setUserInfo', res.data)
|
||||
if (res.data.flag != 1) {
|
||||
Message.error('您的账号未激活或已失效,请激活后使用')
|
||||
this.$store.commit('setActiveDlgShow', true)
|
||||
return;
|
||||
}
|
||||
if (res.data.type != 4 && this.$store.state.mallId != this.$store.state.userInfo.mallId) {
|
||||
Message.error('您当前登录的TEMU账号与会员绑定账号不一致')
|
||||
return;
|
||||
}
|
||||
if (this.ids.length <= 0) {
|
||||
Message.error('请选择抢仓商品');
|
||||
return;
|
||||
}
|
||||
this.isBegin = true;
|
||||
this.timer = [];
|
||||
|
||||
for (let i = 0;i < this.ids.length; i++) {
|
||||
let t = setInterval(this.robFunc(this.ids[i]), 1000);
|
||||
this.timer.push({id: this.ids[i], timer: t})
|
||||
}
|
||||
|
||||
let _this = this;
|
||||
let tt = setInterval(function() {
|
||||
if (_this.ids.length == 0) {
|
||||
_this.isBegin = false;
|
||||
clearInterval(tt);
|
||||
}
|
||||
}, 1000)
|
||||
} else {
|
||||
console.log("获取用户信息失败")
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
robFunc(sn) {
|
||||
let _this = this;
|
||||
return function() {
|
||||
_this.rob(sn);
|
||||
}
|
||||
},
|
||||
rob(sn) {
|
||||
sendChromeAPIMessage({
|
||||
url: 'oms/bg/venom/api/supplier/purchase/manager/joinDeliveryGoodsOrderPlatform',
|
||||
needMallId: true,
|
||||
data: {
|
||||
"subPurchaseOrderSn": sn
|
||||
}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
for (let i = 0;i < this.ids.length; i++) {
|
||||
if (this.ids[i] == sn) {
|
||||
this.ids.splice(i, 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let t = this.timer.filter((item) => {
|
||||
return item.id == sn;
|
||||
})
|
||||
if (t.length > 0) {
|
||||
clearInterval(t[0].timer)
|
||||
}
|
||||
|
||||
for (let j = 0; j < this.tableData.length; j++) {
|
||||
if (this.tableData[j].subPurchaseOrderSn == sn) {
|
||||
this.sendSms(this.tableData[j].productName);
|
||||
this.sendNotification(this.tableData[j].productName);
|
||||
this.tableData.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let j = 0; j < this.tableData.length; j++) {
|
||||
if (this.tableData[j].subPurchaseOrderSn == sn) {
|
||||
this.tableData[j].robTotal ++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
sendSms(productName) {
|
||||
this.$http.post(`/api/sms/sendSuccessSms`, null, {
|
||||
params: {
|
||||
productName: productName
|
||||
}
|
||||
}).then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
},
|
||||
sendNotification(productName) {
|
||||
sendChromeNotification({productName: productName})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.order-manage_productInfo__1pD83>img {
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 60px;
|
||||
}
|
||||
.order-manage_productInfo__1pD83 {
|
||||
min-height: 60px;
|
||||
padding-left: 70px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.order-manage_productInfo__1pD83>div:nth-child(2) {
|
||||
color: rgba(0,0,0,.8);
|
||||
}
|
||||
|
||||
.outerWrapper {
|
||||
margin: 4px 4px 0px 0px;
|
||||
}
|
||||
|
||||
.dot-module__dot___M-RuH .dot-module__circle___2l2UV {
|
||||
flex-shrink: 0;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.dot-module__dot___M-RuH {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.index-module__image-preview___2fiZX .index-module__img___p3B1N {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-color: #f5f5f5;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--bc-Table-emptyTextColor);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.order-manage_skuInfo__FW-Nd, .order-manage_skuInfo__FW-Nd .order-manage_contentInfo__1Cjd6 {
|
||||
display: -webkit-flex;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.order-manage_skuInfo__FW-Nd .order-manage_contentInfo__1Cjd6 {
|
||||
-webkit-flex-direction: column;
|
||||
-moz-box-orient: vertical;
|
||||
-moz-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
color: rgba(0,0,0,.4);
|
||||
-webkit-flex: 1 0 auto;
|
||||
-moz-box-flex: 1;
|
||||
-ms-flex: 1 0 auto;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.order-manage_skuInfo__FW-Nd .order-manage_contentInfo__1Cjd6 .order-manage_title__1VTO5 {
|
||||
-webkit-flex-wrap: wrap;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
color: rgba(0,0,0,.8);
|
||||
}
|
||||
</style>
|
||||
69
src/view/Statistics.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<ai-list class="list">
|
||||
<ai-title
|
||||
slot="title"
|
||||
title="数据统计"
|
||||
isShowBottomBorder>
|
||||
</ai-title>
|
||||
<template slot="content">
|
||||
<ai-card title="数据统计">
|
||||
<div id="chart1"></div>
|
||||
</ai-card>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Line } from '@antv/g2plot'
|
||||
import {sendChromeAPIMessage} from '@/api/chromeApi'
|
||||
|
||||
export default {
|
||||
name: 'ExportSaleData',
|
||||
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
this.initChart1()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
initChart1 () {
|
||||
const data = [
|
||||
{ year: '1991', value: 3 },
|
||||
{ year: '1992', value: 4 },
|
||||
{ year: '1993', value: 3.5 },
|
||||
{ year: '1994', value: 5 },
|
||||
{ year: '1995', value: 4.9 },
|
||||
{ year: '1996', value: 6 },
|
||||
{ year: '1997', value: 7 },
|
||||
{ year: '1998', value: 9 },
|
||||
{ year: '1999', value: 13 }
|
||||
]
|
||||
|
||||
const line = new Line('chart1', {
|
||||
data,
|
||||
xField: 'year',
|
||||
yField: 'value',
|
||||
});
|
||||
|
||||
line.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.list {
|
||||
::v-deep .ai-list__content .ai-list__content--right-wrapper {
|
||||
border-radius: 0;
|
||||
padding: 0!important;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
247
src/view/Welcome.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<div class="admin-home" v-loading="isLoading">
|
||||
<ai-card hideTitle>
|
||||
<div class="content">
|
||||
<p>TEMU助手是为致力于“拼多多跨境卖家中心”通过自动化的方式,提高TEMU运营管理效率。</p>
|
||||
<p>在使用卖家中心过程中,有觉得不方便之处,或者重复劳动力的地方可与我们沟通,我们竭尽全力为大家打造实用的智能化工具。</p>
|
||||
<p>欢迎大家扫描下方二维码与我们沟通交流,获取最新智能化研发进展,一起经营跨境电商,让中国货走向全世界</p>
|
||||
</div>
|
||||
</ai-card>
|
||||
<div class="middle">
|
||||
<ai-card title="通知公告">
|
||||
<div class="list">
|
||||
<div class="item" v-for="(item, index) in noticeList" :key="index" @click="info = item, isShow = true">
|
||||
<div class="left">
|
||||
<el-tag size="small" effect="dark" :type="item.level === '0' ? '' : 'danger'">{{ item.level === '0' ? '一般' : '重要' }}</el-tag>
|
||||
<h2 :title="item.title">{{ item.title }}</h2>
|
||||
</div>
|
||||
<span>{{ item.createTime }}</span>
|
||||
<!-- <img src="../assets/images/right-b.png" /> -->
|
||||
</div>
|
||||
<AiEmpty v-if="!noticeList.length"></AiEmpty>
|
||||
</div>
|
||||
</ai-card>
|
||||
<ai-card title="更新记录">
|
||||
<div class="list">
|
||||
<div class="item" v-for="(item, index) in changeLogList" :key="index" @click="info = item, isShow = true">
|
||||
<div class="left">
|
||||
<el-tag v-if="version !== item.varsion" size="small" effect="dark" type="danger">新版本</el-tag>
|
||||
<h2 :title="item.title">{{ item.title }}</h2>
|
||||
</div>
|
||||
<span>{{ item.createTime }}</span>
|
||||
</div>
|
||||
<AiEmpty v-if="!changeLogList.length"></AiEmpty>
|
||||
</div>
|
||||
</ai-card>
|
||||
</div>
|
||||
<ai-card title="常用工具" v-if="false">
|
||||
<div class="">
|
||||
dsad
|
||||
</div>
|
||||
</ai-card>
|
||||
<AiDialog
|
||||
title="详情"
|
||||
:visible.sync="isShow"
|
||||
:close-on-click-modal="false"
|
||||
customFooter
|
||||
:show-close="!isImportant"
|
||||
width="690">
|
||||
<div class="news">
|
||||
<h2>{{ info.title }}</h2>
|
||||
<p>{{ info.content }}</p>
|
||||
</div>
|
||||
<div class="dialog-footer" slot="footer">
|
||||
<el-button @click="read" type="primary" v-if="!info.varsion">{{ isImportant ? '我已阅读' : '关闭' }}{{ info.version }}</el-button>
|
||||
<el-button @click="download" type="primary" style="width: 140px" v-if="info.varsion && info.varsion !== version">下载新版本</el-button>
|
||||
</div>
|
||||
</AiDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminHome',
|
||||
|
||||
data () {
|
||||
return {
|
||||
noticeList: [],
|
||||
isLoading: false,
|
||||
isShow: false,
|
||||
info: {},
|
||||
changeLogList: [],
|
||||
isImportant: false,
|
||||
version: ''
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
const devVersion = require('../manifest.development.json').version
|
||||
const prodVersion = require('../manifest.production.json').version
|
||||
this.version = process.env.NODE_ENV === 'production' ? prodVersion : devVersion
|
||||
console.log(this.version)
|
||||
this.getNoticeList()
|
||||
this.getChangelog()
|
||||
this.getMyNewestNotice()
|
||||
},
|
||||
|
||||
methods: {
|
||||
getNoticeList () {
|
||||
this.$http.post('/api/notice/page?size=20').then(res => {
|
||||
if (res.code === 0) {
|
||||
this.noticeList = res.data.records
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
download () {
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
link.href = this.info.downloadUrl
|
||||
link.setAttribute('target', '_blank')
|
||||
link.setAttribute('download', `TEMU助手-${this.info.varsion}.zip`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
},
|
||||
|
||||
read () {
|
||||
if (this.isImportant) {
|
||||
this.$http.post('/api/notice/read').then(res => {
|
||||
if (res.code === 0) {
|
||||
this.isImportant = false
|
||||
this.isShow = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.isShow = false
|
||||
}
|
||||
},
|
||||
|
||||
getMyNewestNotice () {
|
||||
this.$http.post('/api/notice/getMyNewestNotice').then(res => {
|
||||
if (res.code === 0) {
|
||||
if (res.data) {
|
||||
this.isImportant = true
|
||||
this.info = res.data
|
||||
this.isShow = true
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getChangelog () {
|
||||
this.$http.post('/api/changelog/page?size=100').then(res => {
|
||||
if (res.code === 0) {
|
||||
this.changeLogList = res.data.records
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.admin-home {
|
||||
padding: 20px 20px;
|
||||
|
||||
.news {
|
||||
min-height: 250px;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5;
|
||||
margin-bottom: 14px;
|
||||
color: #333;
|
||||
font-size: 15px;
|
||||
text-indent: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
p {
|
||||
line-height: 1.3;
|
||||
margin-bottom: 14px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.list {
|
||||
overflow: auto;
|
||||
padding-bottom: 20px;
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 49px;
|
||||
cursor: pointer;
|
||||
color: #1f2635;
|
||||
transition: all .3s ease-in-out;
|
||||
border-bottom: 1px solid #f4f4f4;
|
||||
|
||||
.left, .right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
max-width: 70%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
& > span {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.middle {
|
||||
display: flex;
|
||||
height: 400px;
|
||||
|
||||
& > section {
|
||||
flex: 1;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
347
src/view/login/Login.vue
Normal file
@@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<div class="body">
|
||||
<div class="middle">
|
||||
<div class="right">
|
||||
<div class="tab">
|
||||
<h2 class="active" @click="currIndex = 0">登录</h2>
|
||||
</div>
|
||||
<el-form :model="form" label-position="top" ref="form" label-width="100px" class="form">
|
||||
<el-form-item
|
||||
prop="mobile"
|
||||
:rules="[{ required: true, message: '请输入手机号', trigger: 'blur' }, { validator: phoneReg, trigger: 'blur' }]">
|
||||
<el-input maxlength="11" placeholder="请输入手机号" v-model="form.mobile" @keyup.enter.native="login"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="password"
|
||||
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
|
||||
<el-input placeholder="请输入密码" v-model="form.password" type="password" @keyup.enter.native="login"></el-input>
|
||||
</el-form-item>
|
||||
<el-button type="primary" style="width: 100%" @click="login" :loading="btnLoading">立即登录</el-button>
|
||||
</el-form>
|
||||
<div class="login-footer">
|
||||
<div class="left">
|
||||
<span>没有账号?</span>
|
||||
<i class="hover" @click="$router.push('/register')">立即注册</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
mobile: '',
|
||||
password: ''
|
||||
},
|
||||
phoneReg: (rule, value, callback) => {
|
||||
if (/^1[0-9]{10,10}$/.test(value)) {
|
||||
return callback()
|
||||
}
|
||||
|
||||
callback(new Error('手机号格式错误'))
|
||||
},
|
||||
timer: null,
|
||||
time: 60,
|
||||
isSend: false,
|
||||
isStart: false,
|
||||
isLoading: false,
|
||||
currIndex: 0,
|
||||
btnLoading: false,
|
||||
loginType: '0'
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
},
|
||||
|
||||
methods: {
|
||||
login () {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.btnLoading = true
|
||||
this.$http.post(`/api/mobile/token`, this.form, {
|
||||
params: {
|
||||
...this.form
|
||||
},
|
||||
headers: {
|
||||
Authorization: 'Basic YXBwOmFwcA=='
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.access_token) {
|
||||
this.$store.commit('setToken', `${res.token_type} ${res.access_token}`)
|
||||
this.$message.success('登录成功')
|
||||
this.$store.dispatch('getUserInfo')
|
||||
|
||||
setTimeout(() => {
|
||||
this.$router.push('/')
|
||||
}, 800)
|
||||
}
|
||||
|
||||
this.btnLoading = false
|
||||
}).catch(() => {
|
||||
this.btnLoading = false
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getCode () {
|
||||
if (this.isSend) {
|
||||
return this.$message.error('验证码已发送')
|
||||
}
|
||||
|
||||
this.$refs.form.validateField('loginname', e => {
|
||||
if (!e) {
|
||||
this.isSend = true
|
||||
this.btnLoading = true
|
||||
this.$http.post(`/api/sms/getRegSmsCode?phone=${this.form.phone}`, {}, {
|
||||
withoutToken: true
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success('验证码发送成功')
|
||||
this.isStart = true
|
||||
this.timer = setInterval(() => {
|
||||
if (this.time === 0) {
|
||||
this.isSend = false
|
||||
this.isStart = false
|
||||
this.time = 60
|
||||
clearInterval(this.timer)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
this.time = this.time - 1
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
this.btnLoading = false
|
||||
this.isSend = false
|
||||
}).catch(() => {
|
||||
this.isSend = false
|
||||
this.btnLoading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onChange (e) {
|
||||
this.$emit('change', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
|
||||
.body {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background-color: #cdc8c8;
|
||||
// background: url(../../assets/images/login/login.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.middle {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 11;
|
||||
width: 1280px;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
& > .left {
|
||||
padding-top: 42px;
|
||||
font-family: MicrosoftYaHei;
|
||||
|
||||
p {
|
||||
line-height: 24px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 13px;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
width: 500px;
|
||||
padding: 63px 40px 42px;
|
||||
box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(6px);
|
||||
overflow: hidden;
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
margin-bottom: 36px;
|
||||
padding-bottom: 17px;
|
||||
border-bottom: 1px solid #DCDFE6;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 81px;
|
||||
height: 2px;
|
||||
background: #1FBAD6;
|
||||
content: ' ';
|
||||
transition: all ease-in-out 0.3s;
|
||||
}
|
||||
|
||||
&.tab-active:after {
|
||||
transform: translateX(138px);
|
||||
}
|
||||
|
||||
h2 {
|
||||
width: 81px;
|
||||
line-height: 24px;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
font-family: SJsuqian;
|
||||
color: #000000;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all ease 0.4s;
|
||||
|
||||
&.active, &:hover {
|
||||
color: #1FBAD6;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-right: 58px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-type {
|
||||
font-size: 14px;
|
||||
font-family: SJsuqian;
|
||||
text-align: center;
|
||||
color: #54657D;
|
||||
cursor: pointer;
|
||||
transition: all ease-in-out 0.4s;
|
||||
|
||||
&:hover {
|
||||
color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 21px;
|
||||
margin-bottom: 38px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-family: SJsuqian;
|
||||
color: #54657D;
|
||||
}
|
||||
|
||||
i {
|
||||
font-family: SJsuqian;
|
||||
color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-form {
|
||||
.el-input__inner {
|
||||
height: 48px;
|
||||
border-color: #DCDFE6;
|
||||
background-color: transparent;
|
||||
|
||||
&:focus, &:hover {
|
||||
border-color: #1FBAD6;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus {
|
||||
border-color: #F56C6C;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
height: 48px;
|
||||
margin-top: 28px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.code-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
|
||||
span {
|
||||
width: 110px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-radius: 4px;
|
||||
transition: all cubic-bezier(0.215, 0.61, 0.355, 1) 0.3s;
|
||||
border: 1px solid #DCDFE6;
|
||||
|
||||
&:hover {
|
||||
border-color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copyright {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
left: 50%;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
letter-spacing: 1px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
367
src/view/login/Register.vue
Normal file
@@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<div class="body">
|
||||
<div class="middle">
|
||||
<div class="right">
|
||||
<div class="tab">
|
||||
<h2 class="active" @click="currIndex = 0">注册</h2>
|
||||
</div>
|
||||
<el-form :model="form" label-position="top" ref="form" label-width="100px" class="form">
|
||||
<el-form-item
|
||||
prop="name"
|
||||
:rules="[{ required: true, message: '请输入姓名', trigger: 'blur' }]">
|
||||
<el-input placeholder="请输入姓名" v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="phone"
|
||||
:rules="[{ required: true, message: '请输入手机号', trigger: 'blur' }, { validator: phoneReg, trigger: 'blur' }]">
|
||||
<el-input maxlength="11" placeholder="请输入手机号" v-model="form.phone"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="password"
|
||||
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
|
||||
<el-input placeholder="请输入密码" type="password" v-model="form.password"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="repassword"
|
||||
:rules="[{ required: true, message: '请再次输入密码', trigger: 'blur' }]">
|
||||
<el-input placeholder="请再次输入密码" type="password" v-model="form.repassword"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="code" :rules="[{ required: true, message: '请输入验证码', trigger: 'blur' }]">
|
||||
<div class="code-item">
|
||||
<el-input style="width: 300px;" maxlength="4" placeholder="请输入验证码" v-model="form.code"></el-input>
|
||||
<span @click="getCode" :loading="btnLoading">{{ isStart ? time + ' S' : '发送验证码' }}</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-button type="primary" style="width: 100%" @click="login" :loading="btnLoading">立即注册</el-button>
|
||||
</el-form>
|
||||
<div class="login-footer">
|
||||
<div class="left">
|
||||
<span>有账号?</span>
|
||||
<i class="hover" @click="$router.back()">返回登录</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
name: '',
|
||||
password: '',
|
||||
repassword: '',
|
||||
code: '',
|
||||
phone: ''
|
||||
},
|
||||
phoneReg: (rule, value, callback) => {
|
||||
if (/^1[0-9]{10,10}$/.test(value)) {
|
||||
return callback()
|
||||
}
|
||||
|
||||
callback(new Error('手机号格式错误'))
|
||||
},
|
||||
timer: null,
|
||||
time: 60,
|
||||
isSend: false,
|
||||
isStart: false,
|
||||
isLoading: false,
|
||||
currIndex: 0,
|
||||
btnLoading: false,
|
||||
loginType: '0'
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
},
|
||||
|
||||
methods: {
|
||||
login () {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.form.password != this.form.repassword) {
|
||||
this.$message.success('两次密码输入不一致');
|
||||
return;
|
||||
}
|
||||
this.btnLoading = true
|
||||
this.$http.post(`/api/malluser/reg`, {
|
||||
...this.form
|
||||
}, {
|
||||
headers: {
|
||||
Authorization: 'Basic cGM6cGM='
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success('注册成功')
|
||||
|
||||
setTimeout(() => {
|
||||
this.$router.replace('/login')
|
||||
}, 500)
|
||||
}
|
||||
|
||||
this.btnLoading = false
|
||||
}).catch(() => {
|
||||
this.btnLoading = false
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getCode () {
|
||||
if (this.isSend) {
|
||||
return this.$message.error('验证码已发送')
|
||||
}
|
||||
|
||||
this.$refs.form.validateField('phone', e => {
|
||||
if (!e) {
|
||||
this.isSend = true
|
||||
this.btnLoading = true
|
||||
this.$http.post(`/api/sms/getRegSmsCode?phone=${this.form.phone}`, {}, {
|
||||
withoutToken: true
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success('验证码发送成功')
|
||||
this.isStart = true
|
||||
this.timer = setInterval(() => {
|
||||
if (this.time === 0) {
|
||||
this.isSend = false
|
||||
this.isStart = false
|
||||
this.time = 60
|
||||
clearInterval(this.timer)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
this.time = this.time - 1
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
this.btnLoading = false
|
||||
this.isSend = false
|
||||
}).catch(() => {
|
||||
this.isSend = false
|
||||
this.btnLoading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onChange (e) {
|
||||
this.$emit('change', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
|
||||
.body {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background-color: #cdc8c8;
|
||||
// background: url(../../assets/images/login/login.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.middle {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 11;
|
||||
width: 1280px;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
& > .left {
|
||||
padding-top: 42px;
|
||||
font-family: MicrosoftYaHei;
|
||||
|
||||
p {
|
||||
line-height: 24px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 13px;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
width: 500px;
|
||||
padding: 63px 40px 42px;
|
||||
box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(6px);
|
||||
overflow: hidden;
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
margin-bottom: 36px;
|
||||
padding-bottom: 17px;
|
||||
border-bottom: 1px solid #DCDFE6;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 81px;
|
||||
height: 2px;
|
||||
background: #1FBAD6;
|
||||
content: ' ';
|
||||
transition: all ease-in-out 0.3s;
|
||||
}
|
||||
|
||||
&.tab-active:after {
|
||||
transform: translateX(138px);
|
||||
}
|
||||
|
||||
h2 {
|
||||
width: 81px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-family: SJsuqian;
|
||||
color: #000000;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all ease 0.4s;
|
||||
|
||||
&.active, &:hover {
|
||||
color: #1FBAD6;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-right: 58px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-type {
|
||||
font-size: 14px;
|
||||
font-family: SJsuqian;
|
||||
text-align: center;
|
||||
color: #54657D;
|
||||
cursor: pointer;
|
||||
transition: all ease-in-out 0.4s;
|
||||
|
||||
&:hover {
|
||||
color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 21px;
|
||||
margin-bottom: 38px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-family: SJsuqian;
|
||||
color: #54657D;
|
||||
}
|
||||
|
||||
i {
|
||||
font-family: SJsuqian;
|
||||
color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-form {
|
||||
.el-input__inner {
|
||||
height: 48px;
|
||||
border-color: #DCDFE6;
|
||||
background-color: transparent;
|
||||
|
||||
&:focus, &:hover {
|
||||
border-color: #1FBAD6;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus {
|
||||
border-color: #F56C6C;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
height: 48px;
|
||||
margin-top: 28px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.code-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
|
||||
span {
|
||||
width: 110px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-radius: 4px;
|
||||
transition: all cubic-bezier(0.215, 0.61, 0.355, 1) 0.3s;
|
||||
border: 1px solid #DCDFE6;
|
||||
|
||||
&:hover {
|
||||
border-color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copyright {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
left: 50%;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
letter-spacing: 1px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
56
src/view/popup.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { sendChromeAPIMessage } from '@/api/chromeApi'
|
||||
import { Message } from 'element-ui'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
component: 'Home'
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (!this.$store.state.token) {
|
||||
this.$router.push('/login')
|
||||
} else {
|
||||
this.$store.dispatch('getUserInfo');
|
||||
sendChromeAPIMessage({url: 'bg/quiet/api/mms/userInfo', needMallId: false, data: {}}).then((res) => {
|
||||
if (res.errorCode == 1000000) {
|
||||
if (res.result.companyList[0].malInfoList.length == 1) {
|
||||
this.$store.commit('setMallId', `${res.result.companyList[0].malInfoList[0].mallId}`)
|
||||
this.$store.commit('setMallName', `${res.result.companyList[0].malInfoList[0].mallName}`)
|
||||
} else {
|
||||
if (!this.$store.state.mallId) {
|
||||
this.$store.commit('setMallId', `${res.result.companyList[0].malInfoList[0].mallId}`)
|
||||
this.$store.commit('setMallName', `${res.result.companyList[0].malInfoList[0].mallName}`)
|
||||
} else {
|
||||
let temp = res.result.companyList[0].malInfoList.filter(item => {
|
||||
return item.mallId == this.$store.state.mallId
|
||||
})
|
||||
if (temp.length == 0) {
|
||||
this.$store.commit('setMallId', `${res.result.companyList[0].malInfoList[0].mallId}`)
|
||||
this.$store.commit('setMallName', `${res.result.companyList[0].malInfoList[0].mallName}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$store.commit('setMallList', res.result.companyList[0].malInfoList)
|
||||
} else {
|
||||
Message.error("【拼多多】" + res.error_msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import '../assets/css/index.scss';
|
||||
</style>
|
||||
77
vue.config.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
// Generate pages object
|
||||
const pages = {}
|
||||
|
||||
function getEntryFile (entryPath) {
|
||||
let files = fs.readdirSync(entryPath)
|
||||
return files
|
||||
}
|
||||
|
||||
const chromeName = getEntryFile(path.resolve(`src/entry`))
|
||||
|
||||
function getFileExtension (filename) {
|
||||
return /[.]/.exec(filename) ? /[^.]+$/.exec(filename)[0] : undefined
|
||||
}
|
||||
chromeName.forEach((name) => {
|
||||
const fileExtension = getFileExtension(name)
|
||||
const fileName = name.replace('.' + fileExtension, '')
|
||||
pages[fileName] = {
|
||||
entry: `src/entry/${name}`,
|
||||
template: 'public/index.html',
|
||||
filename: `${fileName}.html`
|
||||
}
|
||||
})
|
||||
|
||||
const isDevMode = process.env.NODE_ENV === 'development'
|
||||
|
||||
module.exports = {
|
||||
pages,
|
||||
filenameHashing: false,
|
||||
chainWebpack: (config) => {
|
||||
config.plugin('copy').use(require('copy-webpack-plugin'), [
|
||||
{
|
||||
patterns: [
|
||||
{
|
||||
from: path.resolve(`src/manifest.${process.env.NODE_ENV}.json`),
|
||||
to: `${path.resolve('dist')}/manifest.json`
|
||||
},
|
||||
{
|
||||
from: path.resolve(`public/`),
|
||||
to: `${path.resolve('dist')}/`
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
},
|
||||
devServer: {
|
||||
port: 8080,
|
||||
open: true,
|
||||
overlay: {
|
||||
warnings: false,
|
||||
errors: true
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://pdd.jjcp52.com',
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
pathRewrite: {
|
||||
'^/api': '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
lintOnSave: false,
|
||||
configureWebpack: {
|
||||
output: {
|
||||
filename: `[name].js`,
|
||||
chunkFilename: `[name].js`
|
||||
},
|
||||
devtool: isDevMode ? 'inline-source-map' : false
|
||||
},
|
||||
css: {
|
||||
extract: false // Make sure the css is the same
|
||||
}
|
||||
}
|
||||