ssss
28
.docker-compose/nginx/conf.d/my.conf
Normal file
@ -0,0 +1,28 @@
|
||||
client_max_body_size 100m;
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
proxy_read_timeout 3600;
|
||||
proxy_send_timeout 3600;
|
||||
#charset koi8-r;
|
||||
#access_log logs/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
rewrite ^/api/(.*)$ /$1 break; #重写
|
||||
proxy_pass http://129.204.101.51:9999; # 设置代理服务器的协议和地址
|
||||
}
|
||||
|
||||
location /api/swagger/index.html {
|
||||
proxy_pass http://129.204.101.51:9999/swagger/index.html;
|
||||
}
|
||||
}
|
||||
6
.env.development
Normal file
@ -0,0 +1,6 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
VITE_APP_BASE_API = 'https://mini-chat.1024tool.vip/'
|
||||
VITE_SERVE = "https://mini-chat.1024tool.vip/"
|
||||
|
||||
|
||||
4
.env.locals
Normal file
@ -0,0 +1,4 @@
|
||||
NODE_ENV = 'locals'
|
||||
|
||||
VITE_APP_BASE_API = '/'
|
||||
VITE_SERVE = "/"
|
||||
5
.env.production
Normal file
@ -0,0 +1,5 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'production'
|
||||
VITE_APP_BASE_API = 'http://scrm.1024tool.vip/'
|
||||
VITE_SERVE = "http://scrm.1024tool.vip/"
|
||||
|
||||
4
.env.test
Normal file
@ -0,0 +1,4 @@
|
||||
NODE_ENV = 'development'
|
||||
|
||||
VITE_APP_BASE_API = 'http://dev-scrm.1024tool.vip/'
|
||||
VITE_SERVE = "http://dev-scrm.1024tool.vip/"
|
||||
26
.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
dev-dist
|
||||
*.local
|
||||
.vite
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
*.zip
|
||||
26
Dockerfile
Normal file
@ -0,0 +1,26 @@
|
||||
FROM registry.cn-shanghai.aliyuncs.com/server1024/node:16.17.1 AS build-stage
|
||||
|
||||
WORKDIR /mht/
|
||||
COPY . .
|
||||
|
||||
# 设置国内源并调试网络问题
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
RUN npm config set network-timeout 600000
|
||||
|
||||
RUN npm install --legacy-peer-deps
|
||||
# 确保 vite 已经全局安装
|
||||
RUN npm install -g vite
|
||||
|
||||
# 检查 vite 是否安装成功
|
||||
RUN vite --version
|
||||
|
||||
ARG ENV=prod
|
||||
|
||||
# 运行构建命令
|
||||
RUN npm run build:${ENV}
|
||||
|
||||
FROM registry.cn-shanghai.aliyuncs.com/server1024/nginx:base AS production-stage
|
||||
|
||||
COPY .docker-compose/nginx/conf.d/my.conf /etc/nginx/conf.d/my.conf
|
||||
COPY --from=build-stage /mht/dist /usr/share/nginx/html
|
||||
RUN ls -al /usr/share/nginx/html
|
||||
36
README.en.md
Normal file
@ -0,0 +1,36 @@
|
||||
# pcf-frontend
|
||||
|
||||
#### Description
|
||||
pcf前端项目
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
37
README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# pcf-frontend
|
||||
|
||||
#### 介绍
|
||||
pcf前端项目
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
|
||||
|
||||
#### 安装教程
|
||||
|
||||
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/)
|
||||
11
auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
|
||||
const ElNotification: typeof import('element-plus/es')['ElNotification']
|
||||
}
|
||||
74
components.d.ts
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ChatRecord: typeof import('./src/components/ChatRecord/index.vue')['default']
|
||||
ElAside: typeof import('element-plus/es')['ElAside']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImage: typeof import('element-plus/es')['ElImage']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||
ElLink: typeof import('element-plus/es')['ElLink']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSelectV2: typeof import('element-plus/es')['ElSelectV2']
|
||||
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
ElTour: typeof import('element-plus/es')['ElTour']
|
||||
ElTourStep: typeof import('element-plus/es')['ElTourStep']
|
||||
IndexUser: typeof import('./src/components/TagClass/indexUser.vue')['default']
|
||||
KeyWords: typeof import('./src/components/KeyWords/index.vue')['default']
|
||||
MaterialPublic: typeof import('./src/components/MaterialPublic/index.vue')['default']
|
||||
MhtMessageBox: typeof import('./src/components/MhtMessageBox/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
Scripts: typeof import('./src/components/Scripts/index.vue')['default']
|
||||
SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
|
||||
Tag: typeof import('./src/components/Scripts/tag.vue')['default']
|
||||
TagClass: typeof import('./src/components/TagClass/index.vue')['default']
|
||||
TagGroup: typeof import('./src/components/Scripts/tagGroup.vue')['default']
|
||||
Upload: typeof import('./src/components/Upload/index.vue')['default']
|
||||
UserCard: typeof import('./src/components/UserCard/index.vue')['default']
|
||||
UserKeyWord: typeof import('./src/components/KeyWords/userKeyWord.vue')['default']
|
||||
UserMessage: typeof import('./src/components/UserMessage/index.vue')['default']
|
||||
Welcome: typeof import('./src/components/Welcome/index.vue')['default']
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
|
||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
}
|
||||
}
|
||||
23
index.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="zh_CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>小程序聊天</title>
|
||||
<script src="//at.alicdn.com/t/c/font_4781615_as5g6ws9ae.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<script>
|
||||
// 解决微信图片不能加载问题
|
||||
(function () {
|
||||
let meta = document.createElement('meta');
|
||||
meta.content = 'no-referrer';
|
||||
meta.name = 'referrer';
|
||||
document.getElementsByTagName('head')[0].appendChild(meta);
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
84
package.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"name": "project",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
"build": "vite build",
|
||||
"build:test": "vite build --mode test",
|
||||
"build:prod": "vite build --mode production",
|
||||
"build:local": "vite build --mode locals",
|
||||
"build:no": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src",
|
||||
"fix": "eslint src --fix",
|
||||
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
|
||||
"lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix",
|
||||
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"axios": "^1.4.0",
|
||||
"bigdecimal": "^0.6.1",
|
||||
"crypto-js": "^4.1.1",
|
||||
"decimal.js": "^10.4.3",
|
||||
"default-passive-events": "^2.0.0",
|
||||
"echarts": "^5.4.3",
|
||||
"echarts-gl": "^1.1.1",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"echarts-wordcloud": "^1.1.3",
|
||||
"element-plus": "^2.8.0",
|
||||
"js-md5": "^0.8.3",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.6",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4",
|
||||
"vue3-cron-plus": "^0.1.9",
|
||||
"vue3-emoji": "^3.0.2",
|
||||
"vue3-json-viewer": "^2.2.2",
|
||||
"vuedraggable": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.22.9",
|
||||
"@iconify-json/ep": "^1.1.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-vue": "^9.15.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"postcss": "^8.4.27",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-pxtorem": "^6.1.0",
|
||||
"postcss-scss": "^4.0.6",
|
||||
"sass": "^1.64.1",
|
||||
"sass-loader": "^13.3.2",
|
||||
"stylelint": "^15.10.2",
|
||||
"stylelint-config-prettier": "^9.0.5",
|
||||
"stylelint-config-recess-order": "^4.3.0",
|
||||
"stylelint-config-recommended-scss": "^12.0.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-config-standard-scss": "^10.0.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-order": "^6.0.3",
|
||||
"stylelint-scss": "^5.0.1",
|
||||
"typescript": "^5.0.2",
|
||||
"unplugin-auto-import": "^0.16.6",
|
||||
"unplugin-icons": "^0.16.5",
|
||||
"unplugin-vue-components": "^0.25.1",
|
||||
"vite": "^4.4.5",
|
||||
"vite-plugin-mock": "2.9.6",
|
||||
"vite-plugin-pwa": "^0.21.1",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^1.8.5"
|
||||
}
|
||||
}
|
||||
6347
pnpm-lock.yaml
generated
Normal file
8
postcss.config.cjs
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
// 'postcss-pxtorem': {
|
||||
// rootValue: 100, // 设置根元素字体大小,1rem = 16px
|
||||
// propList: ['*'], // 将所有属性都转换成rem
|
||||
// },
|
||||
},
|
||||
};
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 13 KiB |
33
src/App.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<!-- 设置element-plus中文 -->
|
||||
<el-config-provider :message="config" :size="size">
|
||||
<router-view v-slot="{ Component }">
|
||||
<div>
|
||||
<component :is="Component" />
|
||||
</div>
|
||||
</router-view>
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
//设置全局主题色插件
|
||||
import { useElementPlusTheme } from '@/layout/tabbar/setting/theme/index'
|
||||
|
||||
//消息提示最大条数
|
||||
const config = reactive({
|
||||
max: 3,
|
||||
})
|
||||
//全局组件大小
|
||||
const size = ref('default')
|
||||
|
||||
onMounted(() => {
|
||||
useElementPlusTheme('#5CC4A7');
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style lang="scss">
|
||||
#app {}
|
||||
</style>
|
||||
57
src/api/ai/index.ts
Normal file
@ -0,0 +1,57 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
export const getAgents = (params) => {
|
||||
return request({
|
||||
url: `api/agents`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const addRobot = (data) => {
|
||||
return request({
|
||||
url: `api/agent`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteRobot = (id) => {
|
||||
return request({
|
||||
url: `api/agent/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
export const editRobot = (id, data) => {
|
||||
return request({
|
||||
url: `api/agent/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const agentGroups = (params) => {
|
||||
return request({
|
||||
url: `api/agent/groups`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const agentContacts = (params) => {
|
||||
return request({
|
||||
url: `api/agent/contacts`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const agentLogs = (params) => {
|
||||
return request({
|
||||
url: `api/agent/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
28
src/api/chat/index.ts
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
export const getUserList = (params) => {
|
||||
return request({
|
||||
url: `admin/app/users`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const send_message = (data) => {
|
||||
return request({
|
||||
url: `admin/send_message`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const get_messages = (params) => {
|
||||
return request({
|
||||
url: `admin/messages`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
149
src/api/customerService/index.ts
Normal file
@ -0,0 +1,149 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
export const robotList = (params) => {
|
||||
return request({
|
||||
url: `admin/list`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const addRobot = (data) => {
|
||||
return request({
|
||||
url: `admin/app/create`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteRobot = (id) => {
|
||||
return request({
|
||||
url: `admin/app/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
export const editRobot = (id, data) => {
|
||||
return request({
|
||||
url: `admin/app/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const robotLog = (params) => {
|
||||
return request({
|
||||
url: `api/robot_monitor/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取机器人消息日志
|
||||
export const robotMsgLog = (params) => {
|
||||
return request({
|
||||
url: `api/robot_message/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const set_pass_request_reply = (data) => {
|
||||
return request({
|
||||
url: `api/contact/set_pass_request_reply`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const pass_request_reply_list = (params) => {
|
||||
return request({
|
||||
url: `api/contact/pass_request_reply_list`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const delete_reply = (id) => {
|
||||
return request({
|
||||
url: `api/contact/pass_request_reply/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export const robot_monitor = (data) => {
|
||||
return request({
|
||||
url: `api/robot_monitor`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取机器人详情
|
||||
export const robot_detail = (id) => {
|
||||
return request({
|
||||
url: `api/robot/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 意图管理
|
||||
|
||||
export const keyWordArr = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/keywords`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteKeyword = (id) => {
|
||||
return request({
|
||||
url: `api/contact/keyword/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询群组智能体
|
||||
|
||||
export const contactAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/agent`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 配置群组智能体
|
||||
|
||||
export const setAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_agent`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除智能体
|
||||
|
||||
export const deleteAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/agent`,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 绑定小程序
|
||||
|
||||
export const bindApps = (id, data) => {
|
||||
return request({
|
||||
url: `admin/rel_app/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
129
src/api/customerService/indexxx.ts
Normal file
@ -0,0 +1,129 @@
|
||||
// 用户管理
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
// 用户列
|
||||
export const userList = (data: any) => {
|
||||
return request({
|
||||
url: `admin/list`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const syncUser = (data: any) => {
|
||||
return request({
|
||||
url: `api/sync/external_contacts`,
|
||||
method: 'post',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 联系人所在群组
|
||||
export const userGroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/groups`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteUser = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/delete`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 添加群成员好友
|
||||
|
||||
export const addFromgroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/add_from_group`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 设置备注
|
||||
|
||||
export const set_remark = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_remark`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 设置描述
|
||||
|
||||
export const set_desc = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_desc`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 邀请列表
|
||||
|
||||
export const inviters = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/inviters`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 邀请列表
|
||||
|
||||
export const tasks = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/tasks`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 客户详情
|
||||
|
||||
export const contactDetail = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact_detail`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 添加手术时间
|
||||
|
||||
export const set_ext1 = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_ext1`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详情
|
||||
export const get_contact_detail = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact_detail`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取聊天记录
|
||||
|
||||
export const getMessages = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/messages`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
155
src/api/group/index.ts
Normal file
@ -0,0 +1,155 @@
|
||||
// 用户管理
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
// 用户列
|
||||
export const groupList = (data) => {
|
||||
return request({
|
||||
url: `api/groups`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
export const groupMemberList = (data) => {
|
||||
return request({
|
||||
url: `api/group_members`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const syncGroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/sync/groups`,
|
||||
method: 'post',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
export const syncGroupMember = (data: any) => {
|
||||
return request({
|
||||
url: `api/sync/group_members`,
|
||||
method: 'post',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const removeMember = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/remove_member`,
|
||||
method: 'post',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
// 邀请好友入群
|
||||
export const inviteMember = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/invite_member`,
|
||||
method: 'post',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
// 群公告
|
||||
|
||||
export const sendTopic = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/send_topic`,
|
||||
method: 'post',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteGroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/delete`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const createGroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/create_external`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const tasks = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/tasks`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询群组智能体
|
||||
|
||||
export const groupAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/agent`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 配置群组智能体
|
||||
|
||||
export const setAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/set_agent`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除智能体
|
||||
|
||||
export const deleteAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/agent`,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 意图管理
|
||||
|
||||
export const keyWordArr = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/keywords`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteKeyword = (id) => {
|
||||
return request({
|
||||
url: `api/group/keyword/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取群组详情
|
||||
export const groupDetail = (data) => {
|
||||
return request({
|
||||
url: `api/group_detail`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取群组聊天记录
|
||||
export const groupChatRecord = (data) => {
|
||||
return request({
|
||||
url: `api/group/messages`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
41
src/api/home/index.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
//国际碳交易市场行情查询
|
||||
// upInternational(data) {
|
||||
// return request({
|
||||
// url: process.env.VUE_APP_URL_BOUNDARY + 'overview/international',
|
||||
// method: 'get',
|
||||
// data
|
||||
// })
|
||||
// },
|
||||
|
||||
export const international = () => {
|
||||
return request({
|
||||
url: `corian-project/overview/international`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export const up = (data: any) => {
|
||||
return request({
|
||||
url: `corian-project/overview/up`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const geenTable = (data: any) => {
|
||||
return request({
|
||||
url: 'corian-project/overview/down',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//新闻发布概览
|
||||
export const findHeadList = () => {
|
||||
return request({
|
||||
url: 'isimp-admin/api/message/findHeadList',
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
49
src/api/keyword/index.ts
Normal file
@ -0,0 +1,49 @@
|
||||
// 用户管理
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
// 意图管理
|
||||
|
||||
export const keywords = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/keywords`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const addKeywords = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/keyword`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const addKeywordsMaterial = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/group/keyword/material/${id}`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const keywordsMaterials = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/keyword/materials`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
export const upKeyword = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/group/keyword/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const deleteKeyword = (id) => {
|
||||
return request({
|
||||
url: `api/group/keyword/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
49
src/api/keyword/userKeyWord.ts
Normal file
@ -0,0 +1,49 @@
|
||||
// 用户管理
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
// 意图管理
|
||||
|
||||
export const keywords = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/keywords`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const addKeywords = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/keyword`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const addKeywordsMaterial = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/contact/keyword/material/${id}`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const keywordsMaterials = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/keyword/materials`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
export const upKeyword = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/contact/keyword/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const deleteKeyword = (id) => {
|
||||
return request({
|
||||
url: `api/contact/keyword/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
76
src/api/login.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export const userLogin = (data: any) => {
|
||||
return request({
|
||||
url: `/admin/login`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const login = (data: any) => {
|
||||
return request({
|
||||
url: `pcf-user/login`,
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data
|
||||
})
|
||||
}
|
||||
// 博世登录
|
||||
|
||||
export const boschLogin = (data: any) => {
|
||||
return request({
|
||||
url: 'bs/login',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const havecodem = (data: any) => {
|
||||
return request({
|
||||
url: 'corian-project/auth/sendCode',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const forgetPass = (data: any) => {
|
||||
return request({
|
||||
url: 'corian-project/auth/resetPassword',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const logout = () => {
|
||||
return request({
|
||||
url: 'corian-project/auth/logout',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
// auth/saveUserDemand
|
||||
export const UserDemand = (data: any) => {
|
||||
return request({
|
||||
url: 'corian-project/auth/saveUserDemand',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// auth/saveUserDemand
|
||||
export const checkToken = () => {
|
||||
return request({
|
||||
url: 'corian-project/auth/checkToken',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 通用单点登录
|
||||
|
||||
export const singleLogin = (data) => {
|
||||
return request({
|
||||
url: 'pcf-user/singleLogin',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
36
src/api/material/index.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// 素材库
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
// 素材列表
|
||||
export const materialList = (data: any) => {
|
||||
return request({
|
||||
url: `api/materials`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const addMaterial = (data: any) => {
|
||||
return request({
|
||||
url: `api/material`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const updataMaterial = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/material/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deletaMaterial = (id) => {
|
||||
return request({
|
||||
url: `api/material/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
44
src/api/message/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
export const sendMessage = (data) => {
|
||||
return request({
|
||||
url: `api/send_text_message`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const sendCardMessage = (data) => {
|
||||
return request({
|
||||
url: `api/send_card_message`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const sendLinkMessage = (data) => {
|
||||
return request({
|
||||
url: `api/send_link_message`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const sendLocationMessage = (data) => {
|
||||
return request({
|
||||
url: `api/send_location_message`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const sendMessages = (data, url) => {
|
||||
return request({
|
||||
url: `api/${url}`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
146
src/api/miniProgram/index.ts
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
export const appList = (params) => {
|
||||
return request({
|
||||
url: `admin/apps`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const addApp = (data) => {
|
||||
return request({
|
||||
url: `admin/app/create`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteRobot = (id) => {
|
||||
return request({
|
||||
url: `admin/app/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
export const editRobot = (id, data) => {
|
||||
return request({
|
||||
url: `admin/app/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const robotLog = (params) => {
|
||||
return request({
|
||||
url: `api/robot_monitor/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取机器人消息日志
|
||||
export const robotMsgLog = (params) => {
|
||||
return request({
|
||||
url: `api/robot_message/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const set_pass_request_reply = (data) => {
|
||||
return request({
|
||||
url: `api/contact/set_pass_request_reply`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const pass_request_reply_list = (params) => {
|
||||
return request({
|
||||
url: `api/contact/pass_request_reply_list`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const delete_reply = (id) => {
|
||||
return request({
|
||||
url: `api/contact/pass_request_reply/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export const robot_monitor = (data) => {
|
||||
return request({
|
||||
url: `api/robot_monitor`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取机器人详情
|
||||
export const robot_detail = (id) => {
|
||||
return request({
|
||||
url: `api/robot/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 意图管理
|
||||
|
||||
export const keyWordArr = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/keywords`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteKeyword = (id) => {
|
||||
return request({
|
||||
url: `api/contact/keyword/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询群组智能体
|
||||
|
||||
export const contactAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/agent`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 配置群组智能体
|
||||
|
||||
export const setAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_agent`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除智能体
|
||||
|
||||
export const deleteAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/agent`,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const getQr = (data: any) => {
|
||||
return request({
|
||||
url: `api/wechat/qrcode`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
137
src/api/robot/index.ts
Normal file
@ -0,0 +1,137 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
export const robotList = (params) => {
|
||||
return request({
|
||||
url: `api/robots`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const addRobot = (data) => {
|
||||
return request({
|
||||
url: `api/robot`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteRobot = (id) => {
|
||||
return request({
|
||||
url: `api/robot/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
export const editRobot = (id, data) => {
|
||||
return request({
|
||||
url: `api/robot/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const robotLog = (params) => {
|
||||
return request({
|
||||
url: `api/robot_monitor/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取机器人消息日志
|
||||
export const robotMsgLog = (params) => {
|
||||
return request({
|
||||
url: `api/robot_message/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const set_pass_request_reply = (data) => {
|
||||
return request({
|
||||
url: `api/contact/set_pass_request_reply`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const pass_request_reply_list = (params) => {
|
||||
return request({
|
||||
url: `api/contact/pass_request_reply_list`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const delete_reply = (id) => {
|
||||
return request({
|
||||
url: `api/contact/pass_request_reply/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export const robot_monitor = (data) => {
|
||||
return request({
|
||||
url: `api/robot_monitor`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取机器人详情
|
||||
export const robot_detail = (id) => {
|
||||
return request({
|
||||
url: `api/robot/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 意图管理
|
||||
|
||||
export const keyWordArr = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/keywords`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteKeyword = (id) => {
|
||||
return request({
|
||||
url: `api/contact/keyword/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询群组智能体
|
||||
|
||||
export const contactAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/agent`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 配置群组智能体
|
||||
|
||||
export const setAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_agent`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除智能体
|
||||
|
||||
export const deleteAgent = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/agent`,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
18
src/api/selfMiniVoice/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
export const minis = (id) => {
|
||||
return request({
|
||||
url: `api/self_mini_program_message/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export const voices = (id) => {
|
||||
return request({
|
||||
url: `api/self_voice_message/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
175
src/api/tags/index.ts
Normal file
@ -0,0 +1,175 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
export const tagGroupList = (data: any) => {
|
||||
return request({
|
||||
url: `api/tag_groups`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const addTagGroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/tag_group`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const upTagGroup = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/tag_group/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteTagGroup = (id) => {
|
||||
return request({
|
||||
url: `api/tag_group/${id}`,
|
||||
method: 'delete'
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const tagList = (data: any) => {
|
||||
return request({
|
||||
url: `api/tags`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const addTag = (data: any) => {
|
||||
return request({
|
||||
url: `api/tag`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const upTag = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/tag/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteTag = (id) => {
|
||||
return request({
|
||||
url: `api/tag/${id}`,
|
||||
method: 'delete'
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
export const tagContacts = (data) => {
|
||||
return request({
|
||||
url: `api/tag/contacts`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 成员绑定标签
|
||||
export const tagBindContact = (data) => {
|
||||
return request({
|
||||
url: `api/tag/bind_contact`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 解绑
|
||||
|
||||
export const tagUnBindContact = (data) => {
|
||||
return request({
|
||||
url: `api/tag/unbind_contact`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 成员标签切换
|
||||
|
||||
export const tagSwitchContact = (data) => {
|
||||
return request({
|
||||
url: `api/tag/switch_contact`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 查询已选择的标签
|
||||
|
||||
export const tagSelected = async (data, ids) => {
|
||||
|
||||
try {
|
||||
const groupRes = await tagGroupList(data);
|
||||
|
||||
if (!groupRes?.list) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const groups = groupRes.list;
|
||||
const tags = await Promise.all(
|
||||
groups.map(async (group) => {
|
||||
const tagsRes = await tagList({
|
||||
robot_id: data.robot_id,
|
||||
tag_group_id: group.id,
|
||||
page: 1,
|
||||
page_size: 100
|
||||
});
|
||||
return tagsRes?.list || [];
|
||||
})
|
||||
).then(results => results.flatMap(result => result));
|
||||
|
||||
const idsSet = new Set(ids.split(','));
|
||||
const selectedList = tags.filter(tag => idsSet.has(String(tag.id)));
|
||||
|
||||
return selectedList;
|
||||
} catch (error) {
|
||||
console.error('Error in tagSelected:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 获取所有标签
|
||||
export const getAllTags = async (data) => {
|
||||
return request({
|
||||
url: `api/tag_and_groups`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取所有标签下的用户
|
||||
export const getAllTagsUsers = async (data) => {
|
||||
return request({
|
||||
url: `api/tag_and_contacts`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 客户绑定标签
|
||||
export const userBindTag = async (data) => {
|
||||
return request({
|
||||
url: `api/tag/modify_contact`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
123
src/api/task/index.ts
Normal file
@ -0,0 +1,123 @@
|
||||
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
export const taskList = (data: any) => {
|
||||
return request({
|
||||
url: `api/tasks`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const addTask = (data: any) => {
|
||||
return request({
|
||||
url: `api/task`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const upTask = (data: any, id) => {
|
||||
return request({
|
||||
url: `api/task/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteTask = (id) => {
|
||||
return request({
|
||||
url: `api/task/${id}`,
|
||||
method: 'delete'
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 群列表
|
||||
export const taskGroups = (data) => {
|
||||
return request({
|
||||
url: `api/task/groups`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
export const taskGroupsAdd = (data, id) => {
|
||||
return request({
|
||||
url: `api/task/group/${id}`,
|
||||
method: 'post',
|
||||
data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const taskFriends = (data) => {
|
||||
return request({
|
||||
url: `api/task/friends`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
export const taskFriendAdd = (data, id) => {
|
||||
return request({
|
||||
url: `api/task/friend/${id}`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 日志
|
||||
export const taskLogs = (data) => {
|
||||
return request({
|
||||
url: `api/task/logs`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 素材
|
||||
export const taskMaterials = (data) => {
|
||||
return request({
|
||||
url: `api/task/materials`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
export const taskMaterialsAdd = (data, id) => {
|
||||
return request({
|
||||
url: `api/task/material/${id}`,
|
||||
method: 'POST',
|
||||
data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 执行任务
|
||||
|
||||
export const execTask = (id) => {
|
||||
return request({
|
||||
url: `api/task/exec/${id}`,
|
||||
method: 'post',
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 停止任务
|
||||
|
||||
export const stopTask = (id) => {
|
||||
return request({
|
||||
url: `api/task/stop/${id}`,
|
||||
method: 'post',
|
||||
|
||||
})
|
||||
}
|
||||
11
src/api/upload/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// 上传文件接口
|
||||
import request from '@/utils/request'
|
||||
export const uploadFile = (data: FormData, url: string) => {
|
||||
return request({
|
||||
url: `admin/upload/image`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
129
src/api/user/index.ts
Normal file
@ -0,0 +1,129 @@
|
||||
// 用户管理
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
// 用户列
|
||||
export const userList = (data: any) => {
|
||||
return request({
|
||||
url: `api/external_contacts`,
|
||||
method: 'get',
|
||||
params: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const syncUser = (data: any) => {
|
||||
return request({
|
||||
url: `api/sync/external_contacts`,
|
||||
method: 'post',
|
||||
data: data
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 联系人所在群组
|
||||
export const userGroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/groups`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteUser = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/delete`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 添加群成员好友
|
||||
|
||||
export const addFromgroup = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/add_from_group`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 设置备注
|
||||
|
||||
export const set_remark = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_remark`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 设置描述
|
||||
|
||||
export const set_desc = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_desc`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 邀请列表
|
||||
|
||||
export const inviters = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/inviters`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 邀请列表
|
||||
|
||||
export const tasks = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/tasks`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 客户详情
|
||||
|
||||
export const contactDetail = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact_detail`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 添加手术时间
|
||||
|
||||
export const set_ext1 = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/set_ext1`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详情
|
||||
export const get_contact_detail = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact_detail`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取聊天记录
|
||||
|
||||
export const getMessages = (data: any) => {
|
||||
return request({
|
||||
url: `api/contact/messages`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
23
src/api/welcome/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
// 用户管理
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
|
||||
// 意图管理
|
||||
|
||||
export const welcomes = (data: any) => {
|
||||
return request({
|
||||
url: `api//group/welcome/materials`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const addWelcomeMaterial = (data: any) => {
|
||||
return request({
|
||||
url: `api/group/welcome/material`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
BIN
src/assets/images/add_robot_bg.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/addess.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
src/assets/images/coze.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
1
src/assets/images/coze.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
src/assets/images/dify.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
src/assets/images/error_images/401.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
src/assets/images/error_images/404.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/images/error_images/cloud.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/assets/images/login_left.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
src/assets/images/logo.png
Normal file
|
After Width: | Height: | Size: 800 KiB |
4
src/assets/images/logo.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="125" height="31" viewBox="0 0 125 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.875 30V0.90909H14.4318C16.5909 0.90909 18.4801 1.33523 20.0994 2.1875C21.7188 3.03977 22.9782 4.23769 23.8778 5.78125C24.7775 7.32481 25.2273 9.12879 25.2273 11.1932C25.2273 13.2765 24.7633 15.0805 23.8352 16.6051C22.9167 18.1297 21.6241 19.304 19.9574 20.1278C18.3002 20.9517 16.3636 21.3636 14.1477 21.3636H6.64773V15.2273H12.5568C13.4848 15.2273 14.2756 15.0663 14.929 14.7443C15.5919 14.4129 16.0985 13.9441 16.4489 13.3381C16.8087 12.732 16.9886 12.017 16.9886 11.1932C16.9886 10.3598 16.8087 9.64962 16.4489 9.0625C16.0985 8.46591 15.5919 8.01136 14.929 7.69886C14.2756 7.37689 13.4848 7.21591 12.5568 7.21591H9.77273V30H1.875Z" fill="#5CC4A7"/>
|
||||
<path d="M46.7756 16.3636H40.767C40.7244 15.8665 40.6108 15.4155 40.4261 15.0107C40.2486 14.6058 40 14.2578 39.6804 13.9666C39.3679 13.6683 38.9879 13.4411 38.5405 13.2848C38.093 13.1214 37.5852 13.0398 37.017 13.0398C36.0227 13.0398 35.1811 13.2812 34.4922 13.7642C33.8104 14.2472 33.2919 14.9396 32.9368 15.8416C32.5888 16.7436 32.4148 17.8267 32.4148 19.0909C32.4148 20.4261 32.5923 21.5447 32.9474 22.4467C33.3097 23.3416 33.8317 24.0163 34.5135 24.4709C35.1953 24.9183 36.0156 25.142 36.9744 25.142C37.5213 25.142 38.0114 25.0746 38.4446 24.9396C38.8778 24.7976 39.2543 24.5952 39.5739 24.3324C39.8935 24.0696 40.1527 23.7536 40.3516 23.3842C40.5575 23.0078 40.696 22.5852 40.767 22.1165L46.7756 22.1591C46.7045 23.0824 46.4453 24.0234 45.9979 24.9822C45.5504 25.9339 44.9148 26.8146 44.0909 27.6243C43.2741 28.4268 42.2621 29.0732 41.0547 29.5632C39.8473 30.0533 38.4446 30.2983 36.8466 30.2983C34.8438 30.2983 33.0469 29.8686 31.456 29.0092C29.8722 28.1499 28.6186 26.8857 27.6953 25.2166C26.7791 23.5476 26.321 21.5057 26.321 19.0909C26.321 16.6619 26.7898 14.6165 27.7273 12.9545C28.6648 11.2855 29.929 10.0249 31.5199 9.17258C33.1108 8.31321 34.8864 7.88352 36.8466 7.88352C38.2244 7.88352 39.4922 8.07173 40.6499 8.44815C41.8075 8.82457 42.8232 9.375 43.6967 10.0994C44.5703 10.8168 45.2734 11.701 45.8061 12.7521C46.3388 13.8033 46.6619 15.0071 46.7756 16.3636ZM49.4629 30V8.18182H64.8038V12.9545H55.3862V16.7045H63.8663V21.4773H55.3862V30H49.4629Z" fill="#CDCCCC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/images/logo_1.png
Normal file
|
After Width: | Height: | Size: 919 KiB |
BIN
src/assets/images/mihoutai.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/assets/images/robot.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/images/robot_1.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
src/assets/images/user_avatar.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
src/assets/images/welcome.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
14
src/assets/set.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true"
|
||||
role="img" class="m-auto ant-menu-item-icon ant-menu-item-icon iconify iconify--solar" width="24"
|
||||
height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor"
|
||||
d="M13 15.4c0-2.074 0-3.111.659-3.756S15.379 11 17.5 11s3.182 0 3.841.644C22 12.29 22 13.326 22 15.4v2.2c0 2.074 0 3.111-.659 3.756S19.621 22 17.5 22s-3.182 0-3.841-.644C13 20.71 13 19.674 13 17.6z"
|
||||
opacity=".5"></path>
|
||||
<path fill="currentColor"
|
||||
d="M2 8.6c0 2.074 0 3.111.659 3.756S4.379 13 6.5 13s3.182 0 3.841-.644C11 11.71 11 10.674 11 8.6V6.4c0-2.074 0-3.111-.659-3.756S8.621 2 6.5 2s-3.182 0-3.841.644C2 3.29 2 4.326 2 6.4zm11-3.1c0-1.087 0-1.63.171-2.06a2.3 2.3 0 0 1 1.218-1.262C14.802 2 15.327 2 16.375 2h2.25c1.048 0 1.573 0 1.986.178c.551.236.99.69 1.218 1.262c.171.43.171.973.171 2.06s0 1.63-.171 2.06a2.3 2.3 0 0 1-1.218 1.262C20.198 9 19.673 9 18.625 9h-2.25c-1.048 0-1.573 0-1.986-.178a2.3 2.3 0 0 1-1.218-1.262C13 7.13 13 6.587 13 5.5">
|
||||
</path>
|
||||
<path fill="currentColor"
|
||||
d="M2 18.5c0 1.087 0 1.63.171 2.06a2.3 2.3 0 0 0 1.218 1.262c.413.178.938.178 1.986.178h2.25c1.048 0 1.573 0 1.986-.178c.551-.236.99-.69 1.218-1.262c.171-.43.171-.973.171-2.06s0-1.63-.171-2.06a2.3 2.3 0 0 0-1.218-1.262C9.198 15 8.673 15 7.625 15h-2.25c-1.048 0-1.573 0-1.986.178c-.551.236-.99.69-1.218 1.262C2 16.87 2 17.413 2 18.5"
|
||||
opacity=".5"></path>
|
||||
</svg>
|
||||
</el-icon>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
src/assets/仪表.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="anticon fill-current inline-block h-[1em] w-[1em] overflow-hidden outline-none ant-menu-item-icon ant-menu-item-icon" aria-label="ic-analysis" style="vertical-align: middle; width: 24px; height: 24px; color: currentcolor;"><title>ic-analysis</title><use xlink:href="#icon-ic-analysis" fill="currentColor"></use></svg>
|
||||
|
After Width: | Height: | Size: 386 B |
1
src/assets/组件.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="m-auto ant-menu-item-icon ant-menu-item-icon iconify iconify--solar" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M13 15.4c0-2.074 0-3.111.659-3.756S15.379 11 17.5 11s3.182 0 3.841.644C22 12.29 22 13.326 22 15.4v2.2c0 2.074 0 3.111-.659 3.756S19.621 22 17.5 22s-3.182 0-3.841-.644C13 20.71 13 19.674 13 17.6z" opacity=".5"></path><path fill="currentColor" d="M2 8.6c0 2.074 0 3.111.659 3.756S4.379 13 6.5 13s3.182 0 3.841-.644C11 11.71 11 10.674 11 8.6V6.4c0-2.074 0-3.111-.659-3.756S8.621 2 6.5 2s-3.182 0-3.841.644C2 3.29 2 4.326 2 6.4zm11-3.1c0-1.087 0-1.63.171-2.06a2.3 2.3 0 0 1 1.218-1.262C14.802 2 15.327 2 16.375 2h2.25c1.048 0 1.573 0 1.986.178c.551.236.99.69 1.218 1.262c.171.43.171.973.171 2.06s0 1.63-.171 2.06a2.3 2.3 0 0 1-1.218 1.262C20.198 9 19.673 9 18.625 9h-2.25c-1.048 0-1.573 0-1.986-.178a2.3 2.3 0 0 1-1.218-1.262C13 7.13 13 6.587 13 5.5"></path><path fill="currentColor" d="M2 18.5c0 1.087 0 1.63.171 2.06a2.3 2.3 0 0 0 1.218 1.262c.413.178.938.178 1.986.178h2.25c1.048 0 1.573 0 1.986-.178c.551-.236.99-.69 1.218-1.262c.171-.43.171-.973.171-2.06s0-1.63-.171-2.06a2.3 2.3 0 0 0-1.218-1.262C9.198 15 8.673 15 7.625 15h-2.25c-1.048 0-1.573 0-1.986.178c-.551.236-.99.69-1.218 1.262C2 16.87 2 17.413 2 18.5" opacity=".5"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/assets/飞机.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="m-auto ant-menu-item-icon ant-menu-item-icon iconify iconify--solar" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M5.796 18.204L21.512 2.488c-.988-.989-2.86-.364-6.606.884l-9.331 3.11c-2.082.694-3.123 1.041-3.439 1.804q-.112.271-.133.564c-.059.824.717 1.6 2.269 3.151l.283.283c.254.254.382.382.478.523c.19.28.297.607.31.945c.008.171-.019.35-.072.705c-.196 1.304-.294 1.956-.179 2.458c.114.495.362.938.704 1.289" clip-rule="evenodd"></path><path fill="currentColor" d="m17.498 18.486l3.13-9.392c1.25-3.745 1.873-5.617.885-6.606L5.797 18.204c.348.356.794.617 1.296.74c.5.122 1.153.033 2.46-.144l.071-.01c.369-.05.553-.075.73-.064c.32.02.63.124.898.303c.147.099.278.23.541.493l.251.251c1.51 1.51 2.266 2.265 3.067 2.226c.22-.01.438-.062.64-.151c.734-.323 1.072-1.336 1.747-3.362" opacity=".5"></path></svg>
|
||||
|
After Width: | Height: | Size: 970 B |
442
src/components/ChatRecord/index.vue
Normal file
@ -0,0 +1,442 @@
|
||||
<template>
|
||||
<div v-for="(element, index) in list">
|
||||
<div class="materia-element" :class="{ robotSend : element.sender_id != sendeInfo.userInfo.user_id }">
|
||||
<el-image v-if="element.sender_id == sendeInfo.robotInfo.wx_id" class="tobot-image" :src="sendeInfo.robotInfo.head_url"></el-image>
|
||||
<template v-else>
|
||||
<el-image v-if="sendeInfo.userInfo.user_id != element.sender_id" class="tobot-image" :src="robot_avatar"></el-image>
|
||||
<el-image v-if="sendeInfo.userInfo.user_id == element.sender_id" class="tobot-image" :src="sendeInfo.userInfo.user_avatar"></el-image>
|
||||
<!-- <span class="tobot-image text-logo" v-else> {{ element.sender_name.charAt(0) }} </span> -->
|
||||
</template>
|
||||
<div class="materia-info">
|
||||
<!-- <span class="robot-name">{{ element.sender_name }}</span> -->
|
||||
<!-- <span v-if="element.sender_id != sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.robotInfo.name }}</span>
|
||||
<span v-if="element.sender_id == sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.userInfo.nickname }}</span> -->
|
||||
<div class="text" v-if="element.msg_type == 1">
|
||||
<pre>{{ element.content.messages }}</pre>
|
||||
</div>
|
||||
<div class="image" v-if="element.msg_type == 2">
|
||||
<el-image :src="element.content.messages" fit="cover"
|
||||
:preview-src-list="[element.content.messages]" preview-teleported>
|
||||
<template #placeholder>
|
||||
<div class="image-slot">Loading<span class="dot">...</span></div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
|
||||
<div class="time">{{ element.send_time }}
|
||||
<el-icon v-if="element._failed" class="retry" @click.stop="emits('retry', element._id)">
|
||||
<svg class="icon" aria-hidden="true"><use xlink:href="#icon-jinggao"></use></svg>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import mihoutai from '@/assets/images/mihoutai.png'
|
||||
import addess_img from '@/assets/images/addess.png'
|
||||
import user_avatar from '@/assets/images/user_avatar.png'
|
||||
import robot_avatar from '@/assets/images/robot.png'
|
||||
const emits = defineEmits(["update:modelValue", "editMateria", "retry"])
|
||||
const props = defineProps({
|
||||
msgList: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
sendeInfo: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
msgType: {
|
||||
type: String,
|
||||
default: 'user'
|
||||
}
|
||||
})
|
||||
const keyWordsText = ref(props.keyWords)
|
||||
const list = ref(props.msgList)
|
||||
watch(() => props.msgList, (val) => {
|
||||
list.value = val
|
||||
})
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.materia-element {
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.tobot-image {
|
||||
float: left;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.text-logo{
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
width: 300px;
|
||||
float: left;
|
||||
|
||||
|
||||
|
||||
.robot-name {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 6px;
|
||||
|
||||
.edit {
|
||||
color: var(--el-color-primary);
|
||||
margin-left: 6px;
|
||||
transform: translateY(3px);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.remove {
|
||||
color: var(--el-color-danger);
|
||||
margin-left: 6px;
|
||||
transform: translateY(3px);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
pre {
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-right: 10px solid #fff;
|
||||
content: '';
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
height: auto;
|
||||
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.gif {
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.audio {
|
||||
width: 100px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
video {
|
||||
width: 180px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.mini {
|
||||
height: 95px;
|
||||
width: 220px;
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
|
||||
.card-user {
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 15px;
|
||||
margin-left: 5px;
|
||||
transform: translateY(7px);
|
||||
display: inline-block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 20px;
|
||||
line-height: 22px;
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.chengxu {
|
||||
height: 103px;
|
||||
|
||||
.el-image {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.name {
|
||||
transform: translateY(0px);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cg-text {
|
||||
display: block;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
padding: 0;
|
||||
// overflow: hidden;
|
||||
width: 220px;
|
||||
|
||||
h4 {
|
||||
line-height: 28px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
font-weight: normal;
|
||||
padding: 0 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
padding: 0 10px;
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 86px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
width: 220px;
|
||||
height: 100px;
|
||||
|
||||
.el-image {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
float: right;
|
||||
// transform: translateY(-17px);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
// margin-top: 13px;
|
||||
height: 50px;
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--el-text-color-secondary);
|
||||
height: 48px;
|
||||
font-size: 12px;
|
||||
float: left;
|
||||
width: calc(100% - 55px);
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
/* 防止文本换行 */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3; //显示两行文本
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
width: 220px;
|
||||
height: 75px;
|
||||
|
||||
.file-box {
|
||||
float: left;
|
||||
width: calc(100% - 50px);
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
float: right;
|
||||
font-size: 40px;
|
||||
color: var(--el-color-primary);
|
||||
transform: translateY(12px);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
transform: translateY(2px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: normal;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
.interval-seconds {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 10px;
|
||||
}
|
||||
.time{
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.robotSend {
|
||||
.tobot-image {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
text-align: right;
|
||||
float: right;
|
||||
.robot-name{
|
||||
text-align: right;
|
||||
}
|
||||
.text {
|
||||
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-left: 10px solid var(--el-color-primary);
|
||||
border-right: 0;
|
||||
content: '';
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.image-slot {
|
||||
height: 100%;
|
||||
background-color: #F0F2F5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
595
src/components/KeyWords/index.vue
Normal file
@ -0,0 +1,595 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <label>配置关键字素材</label> -->
|
||||
<div class="form-content">
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules">
|
||||
<el-form-item label="关键字" prop="keyword">
|
||||
<el-input v-model="ruleForm.keyword" placeholder="请输入关键字"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-button type="primary" icon="Plus" @click="addhuasu">添加素材</el-button>
|
||||
<div class="meaasge">
|
||||
<Scripts :disabled="false" :keyWords="ruleForm.keyword" v-model="materiaList" :materiaList="materiaList" :robotInfo="userRobotStore.robotInfo" @editMateria="editMateria"></Scripts>
|
||||
<!-- <el-scrollbar>
|
||||
<ul>
|
||||
<li class="materia-item keyword" v-if="ruleForm.keyword">
|
||||
<el-image class="tobot-image" :src="mihoutai"></el-image>
|
||||
<div class="materia-info">
|
||||
<div class="text">
|
||||
{{ ruleForm.keyword }}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="materia-item" v-for="(item, index) in materiaList" :key="item.id">
|
||||
<template v-if="item.type != 11">
|
||||
<el-image class="tobot-image" :src="userRobotStore.robotInfo.head_url"></el-image>
|
||||
<div class="materia-info">
|
||||
<span class="robot-name">{{ userRobotStore.robotInfo.name }}
|
||||
<el-icon class="remove" @click="removeMateria(index)">
|
||||
<Delete />
|
||||
</el-icon></span>
|
||||
|
||||
<div class="text" v-if="item.type == 1">
|
||||
{{ item.content.message }}
|
||||
</div>
|
||||
|
||||
<div class="image" v-if="item.type == 2">
|
||||
<el-image :src="item.content.img_url" fit="cover"
|
||||
:preview-src-list="[item.content.img_url]" preview-teleported></el-image>
|
||||
</div>
|
||||
<div class="gif" v-if="item.type == 8">
|
||||
<el-image :src="item.content.img_url" fit="cover"
|
||||
:preview-src-list="[item.content.img_url]" preview-teleported></el-image>
|
||||
</div>
|
||||
<div class="text audio" v-if="item.type == 3">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yuyin"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
|
||||
{{ item.content.voice_time }} ''
|
||||
</div>
|
||||
<div class="video" v-if="item.type == 4">
|
||||
<video :src="item.content.file_url" controls></video>
|
||||
</div>
|
||||
<div class="text mini" v-if="item.type == 9">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="item.content.userInfo?.avatar"></el-image>
|
||||
<span class="name">{{ item.content.userInfo.nickname
|
||||
}}</span>
|
||||
</div>
|
||||
<p>个人名片</p>
|
||||
|
||||
</div>
|
||||
<div class="text mini chengxu" v-if="item.type == 5">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="item.content.head_img_url"></el-image>
|
||||
<span class="name">{{ item.content.des }}</span>
|
||||
</div>
|
||||
<span class="cg-text">{{ item.content.title }}</span>
|
||||
<p><el-icon style="color: rgb(109, 109.1, 203.1);">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaochengxu1"></use>
|
||||
</svg>
|
||||
</el-icon>小程序</p>
|
||||
|
||||
</div>
|
||||
<div class="text address" v-if="item.type == 6">
|
||||
<h4 style="margin-bottom: 0;">{{ item.content.address }}</h4>
|
||||
<p style="margin-bottom: 5px;line-height: 18px;">{{
|
||||
item.content.detailed_address }}</p>
|
||||
<el-image class="" :src="addess_png" fit="cover"></el-image>
|
||||
</div>
|
||||
<div class="text link" v-if="item.type == 7">
|
||||
<h4 @click.stop="openLink(item.content.url)">
|
||||
{{ item.content.title }}
|
||||
</h4>
|
||||
<div @click.stop="openLink(item.content.url)">
|
||||
<p>{{ item.content.content }}</p>
|
||||
<el-image class="" :src="item.content.img_url" fit="cover"></el-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text file" v-if="item.type == 10">
|
||||
<div class="file-box">
|
||||
<label>{{ item.content.file_name }}</label>
|
||||
<span>{{ item.content.file_size }}</span>
|
||||
</div>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="interval-seconds">发送间隔时间:{{ item.interval_seconds }}s</p>
|
||||
</div>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
</el-scrollbar> -->
|
||||
</div>
|
||||
|
||||
<div class="btn-box">
|
||||
<el-button type="primary" :class="{ 'no-click': mhtLoading }" @click="save">
|
||||
<el-icon v-if="mhtLoading" class="mht-loading" style="margin-right: 4px;">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<el-icon v-else style="margin-right: 4px;">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-baocun"></use>
|
||||
</svg></el-icon>
|
||||
保 存
|
||||
</el-button>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible" title="配置素材" width="1000px" align-center destroy-on-close>
|
||||
<div class="dialog-box">
|
||||
|
||||
<UserMessage :isHs="true" @hideMessage="hideMessage" :isKeyWords="true" keyWordsType="group" :room_id="groupInfo.room_id" :editData="editData">
|
||||
</UserMessage>
|
||||
|
||||
</div>
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { keywords, addKeywords, addKeywordsMaterial, keywordsMaterials, upKeyword, deleteKeyword } from '@/api/keyword'
|
||||
import robotStore from '@/store/modules/robotStore'
|
||||
import mihoutai from '@/assets/images/mihoutai.png'
|
||||
const userRobotStore = robotStore()
|
||||
const ruleFormRef = ref()
|
||||
const ruleForm = ref({})
|
||||
const rules = ref({
|
||||
keyword: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入关键字',
|
||||
trigger: 'blur',
|
||||
}
|
||||
],
|
||||
|
||||
})
|
||||
const props = defineProps({
|
||||
groupInfo: {},
|
||||
keywordInfo: {}
|
||||
})
|
||||
// let keyword_id = ''
|
||||
const materiaList = ref([])
|
||||
const getMaterias = async () => {
|
||||
const res = await keywordsMaterials({
|
||||
keyword_id: props.keywordInfo?.id,
|
||||
page: 1,
|
||||
page_size: 100
|
||||
})
|
||||
materiaList.value = res.list.map((el) => {
|
||||
el.content = JSON.parse(el.content)
|
||||
return el
|
||||
})
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const addhuasu = () => {
|
||||
dialogVisible.value = true
|
||||
editData.value = null
|
||||
}
|
||||
const hideMessage = (e) => {
|
||||
dialogVisible.value = false
|
||||
if(editData.value == null){
|
||||
materiaList.value.push(e)
|
||||
|
||||
}
|
||||
}
|
||||
const emits = defineEmits(['hideKey'])
|
||||
|
||||
const mhtLoading = ref(false)
|
||||
const save = async () => {
|
||||
await ruleFormRef.value.validate(async (valid, fields) => {
|
||||
if (valid) {
|
||||
if (materiaList.value.length == 0) {
|
||||
ElNotification({
|
||||
type: 'warning',
|
||||
message: '请至少添加一条素材'
|
||||
})
|
||||
return
|
||||
}
|
||||
let key_id = ''
|
||||
mhtLoading.value = true
|
||||
try {
|
||||
|
||||
|
||||
if (props.keywordInfo?.id) {
|
||||
key_id = props.keywordInfo?.id
|
||||
await upKeyword({
|
||||
"keyword": ruleForm.value.keyword,
|
||||
|
||||
}, key_id)
|
||||
} else {
|
||||
const resKeys = await addKeywords({
|
||||
"keyword": ruleForm.value.keyword,
|
||||
"robot_id": userRobotStore.robotInfo.id,
|
||||
"room_id": props.groupInfo.room_id
|
||||
})
|
||||
key_id = resKeys.id
|
||||
}
|
||||
|
||||
const resMaterial = await addKeywordsMaterial({
|
||||
"material_list": materiaList.value.map((el) => {
|
||||
|
||||
return {
|
||||
content: JSON.stringify(el.content),
|
||||
type: el.type,
|
||||
interval_seconds: el.interval_seconds
|
||||
}
|
||||
})
|
||||
}, key_id)
|
||||
mhtLoading.value = false
|
||||
emits('hideKey')
|
||||
} catch {
|
||||
mhtLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const removeMateria = (index) => {
|
||||
materiaList.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const editData = ref(null)
|
||||
const editMateria = (item) => {
|
||||
dialogVisible.value = true
|
||||
editData.value = item
|
||||
}
|
||||
onMounted(() => {
|
||||
if (props.keywordInfo?.id) {
|
||||
ruleForm.value.keyword = props.keywordInfo.keyword
|
||||
getMaterias()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.meaasge {
|
||||
height: 400px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
padding: 25px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.materia-item {
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.tobot-image {
|
||||
float: left;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
width: 300px;
|
||||
float: left;
|
||||
|
||||
.remove {
|
||||
color: var(--el-color-danger);
|
||||
margin-left: 6px;
|
||||
transform: translateY(3px);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.robot-name {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-right: 10px solid #fff;
|
||||
content: '';
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
height: auto;
|
||||
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.gif {
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.audio {
|
||||
width: 100px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
video {
|
||||
width: 180px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.mini {
|
||||
height: 95px;
|
||||
width: 220px;
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
|
||||
.card-user {
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 15px;
|
||||
margin-left: 5px;
|
||||
transform: translateY(7px);
|
||||
display: inline-block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 20px;
|
||||
line-height: 22px;
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.chengxu {
|
||||
height: 103px;
|
||||
|
||||
.el-image {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.name {
|
||||
transform: translateY(0px);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cg-text {
|
||||
display: block;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
padding: 0;
|
||||
// overflow: hidden;
|
||||
width: 220px;
|
||||
|
||||
h4 {
|
||||
line-height: 28px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
font-weight: normal;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 86px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
width: 220px;
|
||||
height: 100px;
|
||||
|
||||
.el-image {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
float: right;
|
||||
// transform: translateY(-17px);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
// margin-top: 13px;
|
||||
height: 50px;
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--el-text-color-secondary);
|
||||
height: 48px;
|
||||
font-size: 12px;
|
||||
float: left;
|
||||
width: calc(100% - 55px);
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
/* 防止文本换行 */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3; //显示两行文本
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
width: 220px;
|
||||
height: 75px;
|
||||
|
||||
.file-box {
|
||||
float: left;
|
||||
width: calc(100% - 50px);
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
float: right;
|
||||
font-size: 40px;
|
||||
color: var(--el-color-primary);
|
||||
transform: translateY(12px);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
transform: translateY(2px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: normal;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
.interval-seconds {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.keyword {
|
||||
.tobot-image {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
text-align: right;
|
||||
float: right;
|
||||
|
||||
.text {
|
||||
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-left: 10px solid #fff;
|
||||
border-right: 0;
|
||||
content: '';
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.btn-box {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
590
src/components/KeyWords/userKeyWord.vue
Normal file
@ -0,0 +1,590 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <label>配置关键字素材</label> -->
|
||||
<div class="form-content">
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules">
|
||||
<el-form-item label="关键字" prop="keyword">
|
||||
<el-input v-model="ruleForm.keyword" placeholder="请输入关键字"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-button type="primary" icon="Plus" @click="addhuasu">添加素材</el-button>
|
||||
<div class="meaasge">
|
||||
<Scripts :disabled="false" :keyWords="ruleForm.keyword" v-model="materiaList" :materiaList="materiaList" :robotInfo="userRobotStore.robotInfo" @editMateria="editMateria"></Scripts>
|
||||
<!-- <el-scrollbar>
|
||||
<ul>
|
||||
<li class="materia-item keyword" v-if="ruleForm.keyword">
|
||||
<el-image class="tobot-image" :src="mihoutai"></el-image>
|
||||
<div class="materia-info">
|
||||
<div class="text">
|
||||
{{ ruleForm.keyword }}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="materia-item" v-for="(item, index) in materiaList" :key="item.id">
|
||||
<template v-if="item.type != 11">
|
||||
<el-image class="tobot-image" :src="userRobotStore.robotInfo.head_url"></el-image>
|
||||
<div class="materia-info">
|
||||
<span class="robot-name">{{ userRobotStore.robotInfo.name }}
|
||||
<el-icon class="remove" @click="removeMateria(index)">
|
||||
<Delete />
|
||||
</el-icon></span>
|
||||
|
||||
<div class="text" v-if="item.type == 1">
|
||||
{{ item.content.message }}
|
||||
</div>
|
||||
|
||||
<div class="image" v-if="item.type == 2">
|
||||
<el-image :src="item.content.img_url" fit="cover"
|
||||
:preview-src-list="[item.content.img_url]" preview-teleported></el-image>
|
||||
</div>
|
||||
<div class="gif" v-if="item.type == 8">
|
||||
<el-image :src="item.content.img_url" fit="cover"
|
||||
:preview-src-list="[item.content.img_url]" preview-teleported></el-image>
|
||||
</div>
|
||||
<div class="text audio" v-if="item.type == 3">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yuyin"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
|
||||
{{ item.content.voice_time }} ''
|
||||
</div>
|
||||
<div class="video" v-if="item.type == 4">
|
||||
<video :src="item.content.file_url" controls></video>
|
||||
</div>
|
||||
<div class="text mini" v-if="item.type == 9">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="item.content.userInfo?.avatar"></el-image>
|
||||
<span class="name">{{ item.content.userInfo.nickname
|
||||
}}</span>
|
||||
</div>
|
||||
<p>个人名片</p>
|
||||
|
||||
</div>
|
||||
<div class="text mini chengxu" v-if="item.type == 5">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="item.content.head_img_url"></el-image>
|
||||
<span class="name">{{ item.content.des }}</span>
|
||||
</div>
|
||||
<span class="cg-text">{{ item.content.title }}</span>
|
||||
<p><el-icon style="color: rgb(109, 109.1, 203.1);">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaochengxu1"></use>
|
||||
</svg>
|
||||
</el-icon>小程序</p>
|
||||
|
||||
</div>
|
||||
<div class="text address" v-if="item.type == 6">
|
||||
<h4 style="margin-bottom: 0;">{{ item.content.address }}</h4>
|
||||
<p style="margin-bottom: 5px;line-height: 18px;">{{
|
||||
item.content.detailed_address }}</p>
|
||||
<el-image class="" :src="addess_png" fit="cover"></el-image>
|
||||
</div>
|
||||
<div class="text link" v-if="item.type == 7">
|
||||
<h4 @click.stop="openLink(item.content.url)">
|
||||
{{ item.content.title }}
|
||||
</h4>
|
||||
<div @click.stop="openLink(item.content.url)">
|
||||
<p>{{ item.content.content }}</p>
|
||||
<el-image class="" :src="item.content.img_url" fit="cover"></el-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text file" v-if="item.type == 10">
|
||||
<div class="file-box">
|
||||
<label>{{ item.content.file_name }}</label>
|
||||
<span>{{ item.content.file_size }}</span>
|
||||
</div>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
|
||||
</div>
|
||||
<p class="interval-seconds">发送间隔时间:{{ item.interval_seconds }}s</p>
|
||||
</div>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
</el-scrollbar> -->
|
||||
</div>
|
||||
|
||||
<div class="btn-box">
|
||||
<el-button type="primary" :class="{ 'no-click': mhtLoading }" @click="save">
|
||||
<el-icon v-if="mhtLoading" class="mht-loading" style="margin-right: 4px;">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<el-icon v-else style="margin-right: 4px;">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-baocun"></use>
|
||||
</svg></el-icon>
|
||||
保 存
|
||||
</el-button>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible" title="配置素材" width="1000px" align-center destroy-on-close>
|
||||
<div class="dialog-box">
|
||||
|
||||
<UserMessage :isHs="true" @hideMessage="hideMessage" :isKeyWords="true" keyWordsType="robot" :editData="editData">
|
||||
</UserMessage>
|
||||
|
||||
</div>
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { keywords, addKeywords, addKeywordsMaterial, keywordsMaterials, upKeyword, deleteKeyword } from '@/api/keyword/userKeyWord.ts'
|
||||
import robotStore from '@/store/modules/robotStore'
|
||||
import mihoutai from '@/assets/images/mihoutai.png'
|
||||
const userRobotStore = robotStore()
|
||||
const ruleFormRef = ref()
|
||||
const ruleForm = ref({})
|
||||
const rules = ref({
|
||||
keyword: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入关键字',
|
||||
trigger: 'blur',
|
||||
}
|
||||
],
|
||||
|
||||
})
|
||||
const props = defineProps({
|
||||
keywordInfo: {}
|
||||
})
|
||||
// let keyword_id = ''
|
||||
const materiaList = ref([])
|
||||
const getMaterias = async () => {
|
||||
const res = await keywordsMaterials({
|
||||
keyword_id: props.keywordInfo?.id,
|
||||
page: 1,
|
||||
page_size: 100
|
||||
})
|
||||
materiaList.value = res.list.map((el) => {
|
||||
el.content = JSON.parse(el.content)
|
||||
return el
|
||||
})
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const addhuasu = () => {
|
||||
dialogVisible.value = true
|
||||
editData.value = null
|
||||
}
|
||||
const hideMessage = (e) => {
|
||||
dialogVisible.value = false
|
||||
if(editData.value == null){
|
||||
materiaList.value.push(e)
|
||||
|
||||
}
|
||||
}
|
||||
const emits = defineEmits(['hideKey'])
|
||||
const mhtLoading = ref(false)
|
||||
const save = async () => {
|
||||
await ruleFormRef.value.validate(async (valid, fields) => {
|
||||
if (valid) {
|
||||
if (materiaList.value.length == 0) {
|
||||
ElNotification({
|
||||
type: 'warning',
|
||||
message: '请至少添加一条素材'
|
||||
})
|
||||
return
|
||||
}
|
||||
let key_id = ''
|
||||
mhtLoading.value = true
|
||||
try {
|
||||
|
||||
if (props.keywordInfo?.id) {
|
||||
key_id = props.keywordInfo?.id
|
||||
await upKeyword({
|
||||
"keyword": ruleForm.value.keyword,
|
||||
|
||||
}, key_id)
|
||||
} else {
|
||||
const resKeys = await addKeywords({
|
||||
"keyword": ruleForm.value.keyword,
|
||||
"robot_id": userRobotStore.robotInfo.id,
|
||||
})
|
||||
key_id = resKeys.id
|
||||
}
|
||||
|
||||
const resMaterial = await addKeywordsMaterial({
|
||||
"material_list": materiaList.value.map((el) => {
|
||||
return {
|
||||
content: JSON.stringify(el.content),
|
||||
type: el.type,
|
||||
interval_seconds: el.interval_seconds
|
||||
}
|
||||
})
|
||||
}, key_id)
|
||||
mhtLoading.value = false
|
||||
emits('hideKey')
|
||||
} catch {
|
||||
mhtLoading.value = false
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
const removeMateria = (index) => {
|
||||
materiaList.value.splice(index, 1)
|
||||
}
|
||||
const editData = ref(null)
|
||||
const editMateria = (item) => {
|
||||
dialogVisible.value = true
|
||||
editData.value = item
|
||||
}
|
||||
onMounted(() => {
|
||||
if (props.keywordInfo?.id) {
|
||||
ruleForm.value.keyword = props.keywordInfo.keyword
|
||||
getMaterias()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.meaasge {
|
||||
height: 400px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
padding: 25px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.materia-item {
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.tobot-image {
|
||||
float: left;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
width: 300px;
|
||||
float: left;
|
||||
|
||||
.remove {
|
||||
color: var(--el-color-danger);
|
||||
margin-left: 6px;
|
||||
transform: translateY(3px);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.robot-name {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-right: 10px solid #fff;
|
||||
content: '';
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
height: auto;
|
||||
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.gif {
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.audio {
|
||||
width: 100px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
video {
|
||||
width: 180px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.mini {
|
||||
height: 95px;
|
||||
width: 220px;
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
|
||||
.card-user {
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 15px;
|
||||
margin-left: 5px;
|
||||
transform: translateY(7px);
|
||||
display: inline-block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 20px;
|
||||
line-height: 22px;
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.chengxu {
|
||||
height: 103px;
|
||||
|
||||
.el-image {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.name {
|
||||
transform: translateY(0px);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cg-text {
|
||||
display: block;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
padding: 0;
|
||||
// overflow: hidden;
|
||||
width: 220px;
|
||||
|
||||
h4 {
|
||||
line-height: 28px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
font-weight: normal;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 86px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
width: 220px;
|
||||
height: 100px;
|
||||
|
||||
.el-image {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
float: right;
|
||||
// transform: translateY(-17px);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
// margin-top: 13px;
|
||||
height: 50px;
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--el-text-color-secondary);
|
||||
height: 48px;
|
||||
font-size: 12px;
|
||||
float: left;
|
||||
width: calc(100% - 55px);
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
/* 防止文本换行 */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3; //显示两行文本
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
width: 220px;
|
||||
height: 75px;
|
||||
|
||||
.file-box {
|
||||
float: left;
|
||||
width: calc(100% - 50px);
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
float: right;
|
||||
font-size: 40px;
|
||||
color: var(--el-color-primary);
|
||||
transform: translateY(12px);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
transform: translateY(2px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: normal;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
.interval-seconds {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.keyword {
|
||||
.tobot-image {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
text-align: right;
|
||||
float: right;
|
||||
|
||||
.text {
|
||||
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-left: 10px solid #fff;
|
||||
border-right: 0;
|
||||
content: '';
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.btn-box {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
750
src/components/MaterialPublic/index.vue
Normal file
@ -0,0 +1,750 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <el-tabs v-if="!isEdit" v-model="ruleForm.type" class="demo-tabs" @tab-change="handleChange">
|
||||
|
||||
<el-tab-pane v-for="item in materialTypes" :label="item.label" :key="item.value" :name="item.value">
|
||||
|
||||
</el-tab-pane>
|
||||
</el-tabs> -->
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input placeholder="请输入标题" v-model="ruleForm.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input type="textarea" placeholder="请输入描述" v-model="ruleForm.description"></el-input>
|
||||
</el-form-item>
|
||||
<!-- 文本 -->
|
||||
<el-form-item label="内容" prop="content.message" v-if="ruleForm.type == 1" :rules="{
|
||||
required: true,
|
||||
message: '请输入文本内容',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="textarea" placeholder="请输入文本内容" v-model="ruleForm.content.message"></el-input>
|
||||
<V3Emoji @click-emoji="onEmojiSelect" :recent="true"></V3Emoji>
|
||||
</el-form-item>
|
||||
<!-- 图片 -->
|
||||
<el-form-item label="图片" prop="content.img_url" v-if="ruleForm.type == 2" :rules="{
|
||||
required: true,
|
||||
message: '请上传图片',
|
||||
trigger: 'change',
|
||||
}">
|
||||
<Upload v-model="ruleForm.content.img_url" type="image" accept="image/*" :action="robotHost"
|
||||
@change="changeFile"
|
||||
@success="uploadSuccess" @error="uploadError"></Upload>
|
||||
</el-form-item>
|
||||
<!-- 卡片 -->
|
||||
<el-form-item label="名片" prop="content.card_user_id" v-if="ruleForm.type == 9" :rules="{
|
||||
required: true,
|
||||
message: '请选择名片',
|
||||
trigger: 'change',
|
||||
}">
|
||||
<el-select-v2 v-model="ruleForm.content.card_user_id" popper-class="costom-select" :options="cardList"
|
||||
placeholder="请选择名片" style="width: 500px" :props="v2props" @change="changeCard" filterable>
|
||||
<template #default="{ item }">
|
||||
<div class="costom-select-item">
|
||||
<el-image :src="item.avatar"></el-image>
|
||||
<span>{{ item.nickname }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer v-if="cardTotal > 100">
|
||||
<el-pagination v-model:current-page="cardPage" :page-size="100" size="small" background
|
||||
layout="prev, pager, next" :total="cardTotal" class="mt-4"
|
||||
@current-change="currentChange" />
|
||||
</template>
|
||||
</el-select-v2>
|
||||
<el-input style="width: 0;height: 0;" v-model="ruleForm.content.card_user_name"></el-input>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<!-- 链接 -->
|
||||
<template v-if="ruleForm.type == 7">
|
||||
<el-form-item label="链接标题" prop="content.title" :rules="{
|
||||
required: true,
|
||||
message: '请输入链接标题',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.title" placeholder="请输入链接标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="链接内容" prop="content.content" :rules="{
|
||||
required: true,
|
||||
message: '请输入链接内容',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="textarea" row="3" v-model="ruleForm.content.content" placeholder="请输入链接内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="链接图片" prop="content.img_url" :rules="{
|
||||
required: true,
|
||||
message: '请上传图片',
|
||||
trigger: 'change',
|
||||
}">
|
||||
<Upload v-model="ruleForm.content.img_url" type="image" accept="image/*" :action="robotHost"
|
||||
@change="changeFile"
|
||||
@success="uploadSuccess" @error="uploadError">
|
||||
</Upload>
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item label="链接链接地址" prop="content.url" :rules="{
|
||||
required: true,
|
||||
message: '请输入链接地址',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.url" placeholder="请输入链接地址" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<!-- 位置 -->
|
||||
<div v-if="ruleForm.type == '6'">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所在地区" prop="content.address" :rules="{
|
||||
required: true,
|
||||
message: '请输入地区',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input id="mapInput" type="text" v-model="ruleForm.content.address"
|
||||
placeholder="请输入地区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="详细地址" prop="content.detailed_address" :rules="{
|
||||
required: true,
|
||||
message: '请输入详细地址门牌号等',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.detailed_address"
|
||||
placeholder="请输入详细地址门牌号等" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
<div id="mapContainer" style="height: 360px; margin-bottom: 20px;"></div>
|
||||
</div>
|
||||
<!-- 小程序 -->
|
||||
<template v-if="ruleForm.type == 5">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="app_id" prop="content.app_id" :rules="{
|
||||
required: true,
|
||||
message: '请输入app_id',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<p class="tip">请在企业微信手机客户端将小程序发送给自己,即可获取。</p>
|
||||
<el-select v-model="ruleForm.content.msg_id" @change="changeAppId" placeholder="请选择"
|
||||
style="width: 80%; margin-right: 12px;">
|
||||
<el-option v-for="item in appList" :key="item.msg_id" :label="item.des"
|
||||
:value="item.msg_id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="getAppList">刷新</el-button>
|
||||
<!-- <el-input type="text" v-model="ruleForm.content.app_id" placeholder="请输入app_id" /> -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="page_path" prop="content.page_path" :rules="{
|
||||
required: true,
|
||||
message: '请输入page_path',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.page_path" placeholder="请输入page_path" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="des" prop="content.des" :rules="{
|
||||
required: true,
|
||||
message: '请输入des',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.des" placeholder="请输入des" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="head_img_url" prop="content.head_img_url" :rules="{
|
||||
required: true,
|
||||
message: '请输入head_img_url',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.head_img_url"
|
||||
placeholder="请输入head_img_url" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="size" prop="content.size" :rules="{
|
||||
required: true,
|
||||
message: '请输入size',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.size" placeholder="请输入size" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="thumb_aes_key" prop="content.thumb_aes_key" :rules="{
|
||||
required: true,
|
||||
message: '请输入thumb_aes_key',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.thumb_aes_key"
|
||||
placeholder="请输入thumb_aes_key" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="thumb_file_id" prop="content.thumb_file_id" :rules="{
|
||||
required: true,
|
||||
message: '请输入thumb_file_id',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.thumb_file_id"
|
||||
placeholder="请输入thumb_file_id" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="thumb_md5" prop="content.thumb_md5" :rules="{
|
||||
required: true,
|
||||
message: '请输入thumb_md5',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.thumb_md5" placeholder="请输入thumb_md5" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="title" prop="content.title" :rules="{
|
||||
required: true,
|
||||
message: '请输入title',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.title" placeholder="请输入title" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="wechat_id" prop="content.wechat_id" :rules="{
|
||||
required: true,
|
||||
message: '请输入wechat_id',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.wechat_id" placeholder="请输入wechat_id" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<!-- 视频 -->
|
||||
<template v-if="ruleForm.type == 4">
|
||||
<el-form-item label="视频文件" prop="content.file_url" :rules="{
|
||||
required: true,
|
||||
message: '请上传视频文件',
|
||||
trigger: 'change',
|
||||
}">
|
||||
<div>
|
||||
<Upload v-model="ruleForm.content.file_url" type="button" accept="video/*" :action="robotHost"
|
||||
@change="changeFile"
|
||||
@success="uploadSuccess" @error="uploadError">
|
||||
</Upload>
|
||||
<p style="margin-top: 10px;">{{ ruleForm.content.file_name }}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
</template>
|
||||
<!-- GIF -->
|
||||
<template v-if="ruleForm.type == 8">
|
||||
<el-form-item label="GIF文件" prop="content.img_url" :rules="{
|
||||
required: true,
|
||||
message: '请上传GIF图片',
|
||||
trigger: 'change',
|
||||
}">
|
||||
<Upload v-model="ruleForm.content.img_url" type="image" accept="image/gif" :action="robotHost"
|
||||
@change="changeFile"
|
||||
@success="uploadSuccess" @error="uploadError">
|
||||
</Upload>
|
||||
</el-form-item>
|
||||
|
||||
</template>
|
||||
<template v-if="ruleForm.type == 10">
|
||||
<el-form-item label="文件" prop="content.file_url" :rules="{
|
||||
required: true,
|
||||
message: '请上传文件',
|
||||
trigger: 'change',
|
||||
}">
|
||||
<div>
|
||||
<Upload v-model="ruleForm.content.file_url" type="button" accept="" :action="robotHost"
|
||||
@change="changeFile"
|
||||
@success="uploadSuccess" @error="uploadError"></Upload>
|
||||
<p style="margin-top: 10px;">{{ ruleForm.content.file_name }}</p>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
</template>
|
||||
<!-- 语音 -->
|
||||
<template v-if="ruleForm.type == 3">
|
||||
<el-form-item label="aes_key" prop="content.aes_key" :rules="{
|
||||
required: true,
|
||||
message: '请输入aes_key',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<p class="tip">请在企业微信手机客户端将语音发送给自己,即可获取。</p>
|
||||
<el-select v-model="ruleForm.content.aes_key" @change="changeVoice" placeholder="请选择"
|
||||
style="width: 60%;margin-right: 12px;">
|
||||
<el-option v-for="item in voiceList" :key="item.aes_key" :label="item.aes_key"
|
||||
:value="item.aes_key">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="getVoiceList">刷新</el-button>
|
||||
<!-- <el-input type="text" v-model="ruleForm.content.aes_key" placeholder="请输入aes_key" /> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="md5" prop="content.md5" :rules="{
|
||||
required: true,
|
||||
message: '请输入md5',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.md5" placeholder="请输入md5" />
|
||||
</el-form-item>
|
||||
<el-form-item label="size" prop="content.size" :rules="{
|
||||
required: true,
|
||||
message: '请输入size',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.size" placeholder="请输入size" />
|
||||
</el-form-item>
|
||||
<el-form-item label="voice_id" prop="content.voice_id" :rules="{
|
||||
required: true,
|
||||
message: '请输入voice_id',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.voice_id" placeholder="请输入voice_id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="voice_time" prop="content.voice_time" :rules="{
|
||||
required: true,
|
||||
message: '请输入voice_time',
|
||||
trigger: 'blur',
|
||||
}">
|
||||
<el-input type="text" v-model="ruleForm.content.voice_time" placeholder="请输入voice_time" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
</el-form>
|
||||
<div class="submit-btn">
|
||||
<el-button type="primary" @click="submitForm" :class="{ 'no-click': mhtLoading }">
|
||||
<el-icon v-if="mhtLoading" class="mht-loading" style="margin-right: 4px;">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
提 交
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
|
||||
import { addMaterial, updataMaterial, deletaMaterial } from '@/api/material'
|
||||
import { userList } from '@/api/user'
|
||||
import { minis, voices } from '@/api/selfMiniVoice'
|
||||
import Upload from '@/components/Upload/index.vue'
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
import robotStore from '@/store/modules/robotStore'
|
||||
import { uploadProps } from 'element-plus'
|
||||
import V3Emoji from "vue3-emoji";
|
||||
import "vue3-emoji/dist/style.css";
|
||||
const emits = defineEmits(['hideMaterial'])
|
||||
const userRobotStore = robotStore()
|
||||
const materialTypes = [{
|
||||
label: '文本',
|
||||
value: 1
|
||||
}, {
|
||||
label: '图片',
|
||||
value: 2
|
||||
}, {
|
||||
label: '语音',
|
||||
value: 3
|
||||
}, {
|
||||
label: '视频',
|
||||
value: 4
|
||||
}, {
|
||||
label: '位置',
|
||||
value: 6
|
||||
}, {
|
||||
label: '链接',
|
||||
value: 7
|
||||
}, {
|
||||
label: 'GIF图片',
|
||||
value: 8
|
||||
}, {
|
||||
label: '名片',
|
||||
value: 9
|
||||
}, {
|
||||
label: '文件',
|
||||
value: 10
|
||||
}]
|
||||
const ruleFormRef = ref()
|
||||
const ruleForm = ref({
|
||||
type: 1,
|
||||
content: {}
|
||||
})
|
||||
const rules = ref({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入标题',
|
||||
trigger: 'blur',
|
||||
}
|
||||
],
|
||||
description: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入描述',
|
||||
trigger: 'blur',
|
||||
}
|
||||
],
|
||||
|
||||
})
|
||||
const v2props = {
|
||||
value: 'user_id',
|
||||
label: 'nickname'
|
||||
}
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
editData: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
type: ''
|
||||
})
|
||||
const appList = ref([])
|
||||
const voiceList = ref([])
|
||||
let isEdit = false
|
||||
|
||||
let map = null
|
||||
let AutoComplete = null
|
||||
var autoOptions = {
|
||||
input: "mapInput"
|
||||
};
|
||||
|
||||
let marker = null
|
||||
|
||||
const onEmojiSelect = (emoji) => {
|
||||
// if (inputRef.value) {
|
||||
// inputRef.value.focus();
|
||||
// const start = inputRef.value.selectionStart;
|
||||
// const end = inputRef.value.selectionEnd;
|
||||
// const text = inputRef.value.value;
|
||||
// inputRef.value.value = `${text.slice(0, start)}${emoji}${text.slice(end)}`;
|
||||
// }
|
||||
if(ruleForm.value.content.message){
|
||||
ruleForm.value.content.message += emoji
|
||||
} else {
|
||||
ruleForm.value.content.message = emoji
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const show = (data) => {
|
||||
isEdit = true
|
||||
ruleForm.value = data
|
||||
|
||||
}
|
||||
const handleChange = () => {
|
||||
ruleForm.value.title = ''
|
||||
ruleForm.value.content = {}
|
||||
ruleForm.value.description = ''
|
||||
}
|
||||
const mhtLoading = ref(false)
|
||||
const submitForm = async () => {
|
||||
|
||||
ruleFormRef.value.validate(async (valid, fields) => {
|
||||
|
||||
if (valid) {
|
||||
|
||||
const copyFrom = JSON.parse(JSON.stringify(ruleForm.value))
|
||||
copyFrom.content = JSON.stringify(copyFrom.content)
|
||||
mhtLoading.value = true
|
||||
if (!isEdit) {
|
||||
await addMaterial(copyFrom)
|
||||
mhtLoading.value = false
|
||||
} else {
|
||||
|
||||
delete copyFrom.id
|
||||
delete copyFrom.created_at
|
||||
delete copyFrom.created_user
|
||||
delete copyFrom.updated_at
|
||||
delete copyFrom.updated_user
|
||||
await updataMaterial(copyFrom, ruleForm.value.id)
|
||||
mhtLoading.value = false
|
||||
}
|
||||
emits('hideMaterial')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const cardList = ref([])
|
||||
|
||||
const cardTotal = ref(0)
|
||||
const cardPage = ref(1)
|
||||
const getCards = async () => {
|
||||
|
||||
const res = await userList({
|
||||
robot_id: userRobotStore.robotInfo.id,
|
||||
page: cardPage.value,
|
||||
page_size: 100
|
||||
})
|
||||
cardList.value = res.list
|
||||
cardTotal.value = res.total
|
||||
}
|
||||
|
||||
const currentChange = (e) => {
|
||||
cardPage.value = e
|
||||
getCards()
|
||||
}
|
||||
const robotHost = computed(() => {
|
||||
return userRobotStore.robotInfo.server_ip
|
||||
})
|
||||
|
||||
const changeCard = (e) => {
|
||||
cardList.value.forEach((el) => {
|
||||
if (e == el.user_id) {
|
||||
ruleForm.value.content.userInfo = el
|
||||
ruleForm.value.content.card_user_name = el.nickname
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
const disabledBtn = ref(false)
|
||||
const changeFile = () => {
|
||||
disabledBtn.value = true
|
||||
}
|
||||
const uploadSuccess = (e) => {
|
||||
ruleForm.value.content.file_name = e.file
|
||||
ruleForm.value.content.file_path = e.file_path
|
||||
ruleForm.value.content.file_size = e.file_size
|
||||
disabledBtn.value = false
|
||||
}
|
||||
const uploadError = () => {
|
||||
disabledBtn.value = false
|
||||
}
|
||||
const changeAppId = (e) => {
|
||||
let data = appList.value.filter((el) => {
|
||||
return el.msg_id == e
|
||||
})[0]
|
||||
ruleForm.value.content = {
|
||||
msg_id: e,
|
||||
app_id: data.app_id,
|
||||
"title": data.title,
|
||||
"des": data.des,
|
||||
"head_img_url": data.head_img_url,
|
||||
"page_path": data.page_path,
|
||||
"wechat_id": data.wechat_id,
|
||||
"thumb_file_id": data.thumb_file_id,
|
||||
"thumb_md5": data.thumb_md5,
|
||||
"thumb_aes_key": data.thumb_aes_key,
|
||||
"size": data.size,
|
||||
"send_time": data.send_time
|
||||
}
|
||||
|
||||
}
|
||||
const changeVoice = (e) => {
|
||||
ruleForm.value.content = e
|
||||
let data = voiceList.value.filter((el) => {
|
||||
return el.aes_key == e
|
||||
})[0]
|
||||
ruleForm.value.content = {
|
||||
"voice_id": data.voice_id,
|
||||
"md5": data.md5,
|
||||
"aes_key": data.aes_key,
|
||||
"size": data.size,
|
||||
"voice_time": data.voice_time,
|
||||
"send_time": data.send_time
|
||||
}
|
||||
}
|
||||
let geocoder = null
|
||||
|
||||
const getVoiceList = async () => {
|
||||
try {
|
||||
const res = await voices(userRobotStore.robotInfo.id)
|
||||
voiceList.value = res.list
|
||||
ElNotification({
|
||||
type: 'success',
|
||||
message: '刷新成功'
|
||||
})
|
||||
} catch {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: '刷新失败'
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
const getAppList = async () => {
|
||||
try {
|
||||
const res = await minis(userRobotStore.robotInfo.id)
|
||||
appList.value = res.list
|
||||
ElNotification({
|
||||
type: 'success',
|
||||
message: '刷新成功'
|
||||
})
|
||||
} catch {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: '刷新失败'
|
||||
})
|
||||
}
|
||||
}
|
||||
defineExpose({ show })
|
||||
onMounted(async () => {
|
||||
ruleForm.value.type = props.type
|
||||
if (props.type == 3) {
|
||||
const res = await voices(userRobotStore.robotInfo.id)
|
||||
voiceList.value = res.list
|
||||
}
|
||||
if (props.type == 5) {
|
||||
const res = await minis(userRobotStore.robotInfo.id)
|
||||
appList.value = res.list
|
||||
}
|
||||
|
||||
if (ruleForm.value.type == 9) {
|
||||
getCards()
|
||||
}
|
||||
if (ruleForm.value.type == '6') {
|
||||
AMapLoader.load({
|
||||
key: '6c581ef31373ac8659ca4db49c6f1032',
|
||||
version: '2.0',
|
||||
// 需要用到的插件
|
||||
plugins: ["AMap.Geocoder", "AMap.AutoComplete", 'AMap.Geolocation', 'AMap.PlaceSearch', 'AMap.Marker'],
|
||||
})
|
||||
.then((AMap) => {
|
||||
map = new AMap.Map('mapContainer', {
|
||||
viewMode: '2D',
|
||||
zoom: 13,
|
||||
|
||||
// mapStyle: 'amap://styles/grey', //设置地图的显示样式
|
||||
center: ['121.475164', '31.228816'],
|
||||
})
|
||||
|
||||
if (marker) {
|
||||
map.remove(marker);
|
||||
}
|
||||
if (isEdit) {
|
||||
map.setFitView();
|
||||
marker = new AMap.Marker({
|
||||
map: map,
|
||||
// icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
|
||||
position: [ruleForm.value.content.longitude, ruleForm.value.content.latitude],
|
||||
// offset: new AMap.Pixel(-13, -30)
|
||||
});
|
||||
map.setFitView();
|
||||
map.add(marker);
|
||||
}
|
||||
// AMap.plugin('AMap.AutoComplete', function () {
|
||||
// var autoOptions = {
|
||||
// city: '010',
|
||||
// input: 'tipinput', //下面设置的input的id
|
||||
// outPutDirAuto: true,
|
||||
// }
|
||||
// var AutoComplete = new AMap.AutoComplete(autoOptions);
|
||||
// AutoComplete.search(function (status, result) {
|
||||
// // 搜索成功时,result即是对应的匹配数据
|
||||
// })
|
||||
// })
|
||||
|
||||
|
||||
// 搜索提示插件
|
||||
AutoComplete = new AMap.AutoComplete(autoOptions);
|
||||
var placeSearch = new AMap.PlaceSearch({
|
||||
map: map
|
||||
}); //构造地点查询类
|
||||
AutoComplete.on("select", select);//注册监听,当选中某条记录时会触发
|
||||
|
||||
function select(e) {
|
||||
placeSearch.setCity(e.poi.adcode);
|
||||
placeSearch.search(e.poi.name); //关键字查询查询
|
||||
|
||||
ruleForm.value.content.address = e.poi.name
|
||||
ruleForm.value.content.detailed_address = e.poi.district + e.poi.address
|
||||
ruleForm.value.content.longitude = e.poi.location.lng
|
||||
ruleForm.value.content.latitude = e.poi.location.lat
|
||||
}
|
||||
//为地图注册click事件获取鼠标点击出的经纬度坐标
|
||||
map.on('click', function (e) {
|
||||
|
||||
|
||||
map.value = e.lnglat.getLng() + ',' + e.lnglat.getLat()
|
||||
// '点击位置:', e.lnglat;
|
||||
// 获取经纬度
|
||||
ruleForm.value.content.longitude = e.lnglat.lng;
|
||||
ruleForm.value.content.latitude = e.lnglat.lat;
|
||||
// 清除点
|
||||
if (marker) {
|
||||
map.remove(marker);
|
||||
}
|
||||
// 标记点
|
||||
// setMapMarker();
|
||||
marker = new AMap.Marker({
|
||||
map: map,
|
||||
// icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
|
||||
position: [ruleForm.value.content.longitude, ruleForm.value.content.latitude],
|
||||
// offset: new AMap.Pixel(-13, -30)
|
||||
});
|
||||
|
||||
// regeoCode();
|
||||
|
||||
geocoder = new AMap.Geocoder({
|
||||
// city: "010", //城市设为北京,默认:“全国”
|
||||
radius: 1000 //范围,默认:500
|
||||
});
|
||||
let lnglat = [e.lnglat.lng, e.lnglat.lat];
|
||||
geocoder.getAddress(lnglat, function (status, result) {
|
||||
if (status === 'complete' && result.regeocode) {
|
||||
let address = result.regeocode.formattedAddress;
|
||||
|
||||
ruleForm.value.content.address = result.regeocode.addressComponent.province + result.regeocode.addressComponent.district
|
||||
ruleForm.value.content.detailed_address = result.regeocode.addressComponent.township + result.regeocode.addressComponent.street + result.regeocode.addressComponent.streetNumber + result.regeocode.addressComponent.neighborhood
|
||||
// '解析的地址',address;
|
||||
let obj = {
|
||||
ParkLongitude: e.lnglat.lng + '',
|
||||
ParkLatitude: e.lnglat.lat + '',
|
||||
address: address
|
||||
}
|
||||
// emit("clickChild", obj);
|
||||
} else {
|
||||
// log.error('根据经纬度查询地址失败')
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// isTrue.value=true
|
||||
|
||||
})
|
||||
.catch((e) => {
|
||||
})
|
||||
}
|
||||
})
|
||||
onUnmounted(() => {
|
||||
map && map.destroy();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.costom-select-item {
|
||||
|
||||
font-size: 14px;
|
||||
|
||||
.el-image {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 8px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tip {
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
// margin-bottom: 10px;
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
</style>
|
||||
50
src/components/MhtMessageBox/index.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" :title="title" width="460px" align-center destroy-on-close @close="close">
|
||||
<div class="message-box">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
{{ boxText }}
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="close">取 消</el-button>
|
||||
<el-button type="primary" @click="confirm" :class="{ 'no-click': mhtLoading }">
|
||||
<el-icon v-if="mhtLoading" class="mht-loading" style="margin-right: 4px;">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
确 认
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
const props = defineProps({
|
||||
boxText: '',
|
||||
title: '',
|
||||
mhtLoading: false,
|
||||
show: false
|
||||
})
|
||||
const dialogVisible = computed(() => {
|
||||
return props.show
|
||||
})
|
||||
|
||||
const emits = defineEmits(['confirm', 'close'])
|
||||
const confirm = ()=>{
|
||||
emits('confirm')
|
||||
}
|
||||
const close = () => {
|
||||
emits('close')
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.message-box{
|
||||
padding: 0 20px;
|
||||
}
|
||||
.el-icon{
|
||||
color: var(--el-color-warning);
|
||||
font-size: 22px;
|
||||
transform: translateY(5px);
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
551
src/components/Scripts/index.vue
Normal file
@ -0,0 +1,551 @@
|
||||
<template>
|
||||
<el-scrollbar>
|
||||
<div class="materia-element keyword" v-if="keyWordsText">
|
||||
<el-image class="tobot-image" :src="mihoutai"></el-image>
|
||||
<div class="materia-info">
|
||||
<div class="text">
|
||||
{{ keyWordsText }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<draggable v-model="list" @update="endDraggable" :disabled="disabled">
|
||||
<template #item="{ element, index }">
|
||||
<div class="materia-element">
|
||||
<el-image class="tobot-image" :src="robotInfo.head_url"></el-image>
|
||||
<div class="materia-info">
|
||||
<span class="robot-name">{{ robotInfo.name }}
|
||||
<el-icon class="edit" @click="editMateria(index, element)" v-if="!disabled">
|
||||
<Edit />
|
||||
</el-icon>
|
||||
<el-icon class="remove" @click="removeMateria(index)" v-if="!disabled">
|
||||
<Delete />
|
||||
</el-icon></span>
|
||||
|
||||
<div class="text" v-if="element.type == 1">
|
||||
<pre>{{ element.content.message }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="image" v-if="element.type == 2">
|
||||
<el-image :src="element.content.img_url" fit="cover"
|
||||
:preview-src-list="[element.content.img_url]" preview-teleported>
|
||||
<template #placeholder>
|
||||
<div class="image-slot">Loading<span class="dot">...</span></div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="gif" v-if="element.type == 8">
|
||||
<el-image :src="element.content.img_url" fit="cover"
|
||||
:preview-src-list="[element.content.img_url]" preview-teleported>
|
||||
<template #placeholder>
|
||||
<div class="image-slot">Loading<span class="dot">...</span></div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="text audio" v-if="element.type == 3">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yuyin"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
|
||||
{{ element.content.voice_time }} ''
|
||||
</div>
|
||||
<div class="video" v-if="element.type == 4">
|
||||
<video :src="element.content.file_url" controls></video>
|
||||
</div>
|
||||
<div class="text mini" v-if="element.type == 9">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="element.content.userInfo?.avatar"></el-image>
|
||||
<span class="name">{{ element.content.userInfo.nickname
|
||||
}}</span>
|
||||
</div>
|
||||
<p>个人名片</p>
|
||||
|
||||
</div>
|
||||
<div class="text mini chengxu" v-if="element.type == 5">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="element.content.head_img_url"></el-image>
|
||||
<span class="name">{{ element.content.des }}</span>
|
||||
</div>
|
||||
<span class="cg-text">{{ element.content.title }}</span>
|
||||
<p><el-icon style="color: rgb(109, 109.1, 203.1);">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaochengxu1"></use>
|
||||
</svg>
|
||||
</el-icon>小程序</p>
|
||||
|
||||
</div>
|
||||
<div class="text address" v-if="element.type == 6">
|
||||
<h4 style="margin-bottom: 0;">{{ element.content.address }}
|
||||
</h4>
|
||||
<p style="margin-bottom: 5px;line-height: 18px;">{{
|
||||
element.content.detailed_address }}</p>
|
||||
<el-image class="" :src="addess_img" fit="cover"></el-image>
|
||||
</div>
|
||||
<div class="text link" v-if="element.type == 7">
|
||||
<h4 @click.stop="openLink(element.content.url)">
|
||||
{{ element.content.title }}
|
||||
</h4>
|
||||
<div @click.stop="openLink(element.content.url)">
|
||||
<p>{{ element.content.content }}</p>
|
||||
<el-image class="" :src="element.content.img_url" fit="cover"></el-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text file" v-if="element.type == 10">
|
||||
<div class="file-box">
|
||||
<label>{{ element.content.file_name }}</label>
|
||||
<span>{{ element.content.file_size }}</span>
|
||||
</div>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
<!-- <el-image class="" :src="'element.content.img_url'"
|
||||
fit="cover"></el-image> -->
|
||||
</div>
|
||||
<div class="text" v-if="element.type == 11">
|
||||
<pre><span v-if="element.content.group.group_id">通知群:<TagGroup :tag_id="element.content.group.group_id"
|
||||
:robotInfo="robotInfo"></TagGroup></span>
|
||||
<span v-if="element.content.contact.contact_id">通知管理员:<Tag :tag_id="element.content.contact.contact_id"
|
||||
:robotInfo="robotInfo"></Tag></span>
|
||||
通知模版: {{ element.content.group.template }}
|
||||
<span v-if="element.content.auto_reply">自动回复内容:{{ element.content.auto_reply }}</span>
|
||||
</pre>
|
||||
</div>
|
||||
<p class="interval-seconds">发送间隔时间:{{ element.interval_seconds }}s
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { contactDetail } from '@/api/user'
|
||||
import { groupDetail } from '@/api/group'
|
||||
import draggable from 'vuedraggable'
|
||||
import mihoutai from '@/assets/images/mihoutai.png'
|
||||
import addess_img from '@/assets/images/addess.png'
|
||||
const emits = defineEmits(["update:modelValue", "editMateria"])
|
||||
const props = defineProps({
|
||||
materiaList: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
robotInfo: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
keyWords: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const keyWordsText = ref(props.keyWords)
|
||||
const list = ref(props.materiaList)
|
||||
watch(() => props.materiaList, (val) => {
|
||||
list.value = val
|
||||
})
|
||||
watch(() => props.keyWords, (val) => {
|
||||
keyWordsText.value = val
|
||||
})
|
||||
|
||||
|
||||
// 拖拽结束
|
||||
const endDraggable = () => {
|
||||
emits("update:modelValue", list.value)
|
||||
}
|
||||
|
||||
// 删除话术
|
||||
const removeMateria = (index) => {
|
||||
list.value.splice(index, 1)
|
||||
emits("update:modelValue", list.value)
|
||||
}
|
||||
|
||||
// 编辑单条话术
|
||||
const editMateria = (index, item) => {
|
||||
emits("editMateria", item)
|
||||
}
|
||||
|
||||
const getContactDetail = async (id) => {
|
||||
const res = await contactDetail({
|
||||
user_id: id,
|
||||
robot_id: props.robotInfo.id
|
||||
})
|
||||
return res.nickname
|
||||
|
||||
}
|
||||
const getGroupDetail = async (id) => {
|
||||
const res = await groupDetail({
|
||||
group_id: id,
|
||||
robot_id: props.robotInfo.id
|
||||
})
|
||||
return res.room_name
|
||||
|
||||
}
|
||||
onMounted(() => {
|
||||
// list.value = props.materiaList
|
||||
console.log(props.keyWords)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.materia-element {
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.tobot-image {
|
||||
float: left;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
width: 300px;
|
||||
float: left;
|
||||
|
||||
|
||||
|
||||
.robot-name {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 6px;
|
||||
|
||||
.edit {
|
||||
color: var(--el-color-primary);
|
||||
margin-left: 6px;
|
||||
transform: translateY(3px);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.remove {
|
||||
color: var(--el-color-danger);
|
||||
margin-left: 6px;
|
||||
transform: translateY(3px);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
pre {
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-right: 10px solid #fff;
|
||||
content: '';
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
height: auto;
|
||||
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.gif {
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.audio {
|
||||
width: 100px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
video {
|
||||
width: 180px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.mini {
|
||||
height: 95px;
|
||||
width: 220px;
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
|
||||
.card-user {
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 15px;
|
||||
margin-left: 5px;
|
||||
transform: translateY(7px);
|
||||
display: inline-block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 20px;
|
||||
line-height: 22px;
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.chengxu {
|
||||
height: 103px;
|
||||
|
||||
.el-image {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.name {
|
||||
transform: translateY(0px);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cg-text {
|
||||
display: block;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
padding: 0;
|
||||
// overflow: hidden;
|
||||
width: 220px;
|
||||
|
||||
h4 {
|
||||
line-height: 28px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
font-weight: normal;
|
||||
padding: 0 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
padding: 0 10px;
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 86px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
width: 220px;
|
||||
height: 100px;
|
||||
|
||||
.el-image {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
float: right;
|
||||
// transform: translateY(-17px);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
// margin-top: 13px;
|
||||
height: 50px;
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--el-text-color-secondary);
|
||||
height: 48px;
|
||||
font-size: 12px;
|
||||
float: left;
|
||||
width: calc(100% - 55px);
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
/* 防止文本换行 */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3; //显示两行文本
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
width: 220px;
|
||||
height: 75px;
|
||||
|
||||
.file-box {
|
||||
float: left;
|
||||
width: calc(100% - 50px);
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
float: right;
|
||||
font-size: 40px;
|
||||
color: var(--el-color-primary);
|
||||
transform: translateY(12px);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
transform: translateY(2px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: normal;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
.interval-seconds {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.keyword {
|
||||
.tobot-image {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
text-align: right;
|
||||
float: right;
|
||||
|
||||
.text {
|
||||
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-left: 10px solid #fff;
|
||||
border-right: 0;
|
||||
content: '';
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.image-slot {
|
||||
height: 100%;
|
||||
background-color: #F0F2F5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
40
src/components/Scripts/tag.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<span>{{ text }}</span>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onBeforeMount, onBeforeUnmount, onUnmounted, watch, nextTick } from 'vue'
|
||||
import { contactDetail } from '@/api/user'
|
||||
import { groupDetail } from '@/api/group'
|
||||
const props = defineProps({
|
||||
tag_id: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
robotInfo: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
|
||||
})
|
||||
const text = ref('')
|
||||
watch(() => props.tag_id, async (val) => {
|
||||
const res = await contactDetail({
|
||||
user_id: val,
|
||||
robot_id: props.robotInfo.id
|
||||
})
|
||||
text.value = res.nickname
|
||||
console.log(res.nickname)
|
||||
})
|
||||
onMounted(() => {
|
||||
if(props.tag_id){
|
||||
contactDetail({
|
||||
user_id: props.tag_id,
|
||||
robot_id: props.robotInfo.id
|
||||
}).then(res => {
|
||||
text.value = res.nickname
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
39
src/components/Scripts/tagGroup.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<span>{{ text }}</span>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onBeforeMount, onBeforeUnmount, onUnmounted, watch, nextTick } from 'vue'
|
||||
import { contactDetail } from '@/api/user'
|
||||
import { groupDetail } from '@/api/group'
|
||||
const props = defineProps({
|
||||
tag_id: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
robotInfo: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
|
||||
})
|
||||
const text = ref('')
|
||||
watch(() => props.tag_id, async (val) => {
|
||||
const res = await groupDetail({
|
||||
group_id: val,
|
||||
robot_id: props.robotInfo.id
|
||||
})
|
||||
text.value = res.room_name
|
||||
})
|
||||
onMounted(() => {
|
||||
if(props.tag_id){
|
||||
groupDetail({
|
||||
group_id: props.tag_id,
|
||||
robot_id: props.robotInfo.id
|
||||
}).then(res => {
|
||||
text.value = res.room_name
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
35
src/components/SvgIcon/index.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg :style="{ width: width, height: height }">
|
||||
<use :xlink:href="prefix + name" :fill="color"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
//xlink:href属性值的前缀
|
||||
prefix: {
|
||||
type: String,
|
||||
default: '#icon-',
|
||||
},
|
||||
//svg矢量图的名字
|
||||
name: String,
|
||||
//svg图标的颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
//svg宽度
|
||||
width: {
|
||||
type: String,
|
||||
default: '16px',
|
||||
},
|
||||
//svg高度
|
||||
height: {
|
||||
type: String,
|
||||
default: '16px',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style scoped></style>
|
||||
136
src/components/TagClass/index.vue
Normal file
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div class="tag-class-container" v-loading="loading">
|
||||
<div v-for="group in tagGroups" :key="group.id" class="tag-group">
|
||||
<div class="group-title">{{ group.name }}:</div>
|
||||
<div class="tags-container">
|
||||
<el-tag
|
||||
v-for="(tag, index) in group.tag_list"
|
||||
:key="tag.id"
|
||||
class="tag-item"
|
||||
:type="getTagIsAct(tag) ? 'primary' : 'info'"
|
||||
@click="toggleTag(tag)"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { tagGroupList, tagList, getAllTags } from '@/api/tags'
|
||||
|
||||
const props = defineProps({
|
||||
robot_id: {
|
||||
type: [Number, String, Object],
|
||||
default: 0
|
||||
},
|
||||
// 可以接收已选择的标签
|
||||
initialSelectedTags: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
is_user: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:selectedTags', 'change', 'hiddenTag'])
|
||||
const loading = ref(false)
|
||||
const tagGroups = ref([])
|
||||
const selectedTags = ref(props.initialSelectedTags)
|
||||
|
||||
// 监听初始选择的标签变化
|
||||
watch(() => props.initialSelectedTags, (newVal) => {
|
||||
console.log(newVal)
|
||||
selectedTags.value = newVal
|
||||
console.log(selectedTags.value )
|
||||
})
|
||||
|
||||
const getTagIsAct = (tag) => {
|
||||
return selectedTags.value.some(el => el.id == tag.id)
|
||||
}
|
||||
// 获取标签树
|
||||
const getTagTree = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getAllTags({
|
||||
robot_id: props.robot_id,
|
||||
name: '',
|
||||
})
|
||||
tagGroups.value = res.list
|
||||
loading.value = false
|
||||
|
||||
} catch (error) {
|
||||
loading.value = false
|
||||
console.error('获取标签树失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 切换标签选择状态
|
||||
const toggleTag = (tag) => {
|
||||
const isAct = selectedTags.value.some(el => el.id == tag.id)
|
||||
if (isAct) {
|
||||
// 如果已选中,则取消选中
|
||||
const index = selectedTags.value.findIndex(el => el.id == tag.id)
|
||||
selectedTags.value.splice(index, 1)
|
||||
} else {
|
||||
// 如果未选中,则选中
|
||||
selectedTags.value.push(tag)
|
||||
}
|
||||
|
||||
// 向父组件发送更新事件
|
||||
emit('update:selectedTags', selectedTags.value)
|
||||
emit('change', selectedTags.value)
|
||||
emit('hiddenTag', selectedTags.tag)
|
||||
|
||||
}
|
||||
|
||||
// 组件挂载时获取标签树
|
||||
onMounted(() => {
|
||||
getTagTree()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tag-class-container {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.tag-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
/* font-weight: bold; */
|
||||
margin-bottom: 12px;
|
||||
/* color: var(--el-text-color-regular); */
|
||||
}
|
||||
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
/* font-size: 14px; */
|
||||
/* padding: 0px 20px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 6px; */
|
||||
cursor: pointer;
|
||||
/* transition: all 0.3s; */
|
||||
}
|
||||
|
||||
/* .tag-item:hover {
|
||||
background-color: #e0e0e0;
|
||||
} */
|
||||
|
||||
/* .tag-item.selected {
|
||||
background-color: var(--el-color-primary);
|
||||
color: white;
|
||||
} */
|
||||
</style>
|
||||
153
src/components/TagClass/indexUser.vue
Normal file
@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="tag-class-container" v-loading="loading">
|
||||
<div v-for="group in tagGroups" :key="group.id" class="tag-group">
|
||||
<div class="group-title">{{ group.name }}:</div>
|
||||
<div class="tags-container">
|
||||
<el-tag
|
||||
v-for="(tag, index) in group.tag_list"
|
||||
:key="tag.id"
|
||||
class="tag-item"
|
||||
:type="tag.is_act ? 'primary' : 'info'"
|
||||
@click="toggleTag(tag, group)"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { tagGroupList, tagList, getAllTags } from '@/api/tags'
|
||||
|
||||
const props = defineProps({
|
||||
robot_id: {
|
||||
type: [Number, String, Object],
|
||||
default: 0
|
||||
},
|
||||
// 可以接收已选择的标签
|
||||
initialSelectedTags: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
is_user: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:selectedTags', 'change', 'hiddenTag'])
|
||||
const loading = ref(false)
|
||||
const tagGroups = ref([])
|
||||
const selectedTags = ref(props.initialSelectedTags)
|
||||
|
||||
// 监听初始选择的标签变化
|
||||
// watch(() => props.initialSelectedTags, (newVal) => {
|
||||
// console.log(newVal)
|
||||
// selectedTags.value = newVal
|
||||
// console.log(selectedTags.value )
|
||||
// })
|
||||
|
||||
const getTagIsAct = () => {
|
||||
// return selectedTags.value.some(el => el.id == tag.id)
|
||||
tagGroups.value.forEach(group => {
|
||||
group.tag_list.forEach(tag => {
|
||||
props.initialSelectedTags.forEach(el => {
|
||||
if(tag.id == el.id){
|
||||
tag.is_act = true
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
// 获取标签树
|
||||
const getTagTree = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getAllTags({
|
||||
robot_id: props.robot_id,
|
||||
name: '',
|
||||
})
|
||||
tagGroups.value = res.list
|
||||
loading.value = false
|
||||
getTagIsAct()
|
||||
} catch (error) {
|
||||
loading.value = false
|
||||
console.error('获取标签树失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 切换标签选择状态
|
||||
const toggleTag = (tag, group) => {
|
||||
tag.is_act = !tag.is_act
|
||||
if(tag.is_act){
|
||||
group.tag_list.forEach(el => {
|
||||
if(el.id != tag.id){
|
||||
el.is_act = false
|
||||
}
|
||||
})
|
||||
}
|
||||
let selectedTags = []
|
||||
tagGroups.value.forEach(el => {
|
||||
el.tag_list.forEach(el => {
|
||||
if(el.is_act){
|
||||
selectedTags.push(el)
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(selectedTags)
|
||||
// 向父组件发送更新事件
|
||||
emit('update:selectedTags', selectedTags)
|
||||
emit('change', selectedTags)
|
||||
emit('hiddenTag', selectedTags)
|
||||
|
||||
}
|
||||
|
||||
// 组件挂载时获取标签树
|
||||
onMounted(() => {
|
||||
getTagTree()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tag-class-container {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.tag-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
/* font-weight: bold; */
|
||||
margin-bottom: 12px;
|
||||
/* color: var(--el-text-color-regular); */
|
||||
}
|
||||
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
/* font-size: 14px; */
|
||||
/* padding: 0px 20px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 6px; */
|
||||
cursor: pointer;
|
||||
/* transition: all 0.3s; */
|
||||
}
|
||||
|
||||
/* .tag-item:hover {
|
||||
background-color: #e0e0e0;
|
||||
} */
|
||||
|
||||
/* .tag-item.selected {
|
||||
background-color: var(--el-color-primary);
|
||||
color: white;
|
||||
} */
|
||||
</style>
|
||||
394
src/components/Upload/index.vue
Normal file
@ -0,0 +1,394 @@
|
||||
<template>
|
||||
<div class="upload" v-if="type == 'image'">
|
||||
<template v-if="listType == 'picture'">
|
||||
<div class="up-list">
|
||||
<div v-for="(item, index) in imglist" :key="item" class="up-after"
|
||||
:style="{ width: width, height: height }" v-loading="index == imglist.length - 1 && loading">
|
||||
<img :src="imglist[index]" />
|
||||
<div class="preview-delete">
|
||||
<el-icon @click="dialogImg(item)">
|
||||
<ZoomIn />
|
||||
</el-icon>
|
||||
<el-icon @click="deleteImgList(index)">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="up-front" :style="{ width: width, height: height }">
|
||||
<input ref="uploadFileImgs" type="file" @change="getFile" multiple :accept="accept" />
|
||||
<slot>
|
||||
<div class="up-tips">
|
||||
<div>
|
||||
<el-icon class="plus-icon">
|
||||
<Plus></Plus>
|
||||
</el-icon>
|
||||
|
||||
<p>
|
||||
上传图片
|
||||
</p>
|
||||
<span>或将文件拖到此处</span>
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
<slot name="image"></slot>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="up-front" v-if="!modelValue" :style="{ width: width, height: height }">
|
||||
<input ref="uploadFileImg" type="file" @change="getFile" multiple :accept="accept" />
|
||||
<slot>
|
||||
<div class="up-tips">
|
||||
<div>
|
||||
<el-icon class="plus-icon">
|
||||
<Plus></Plus>
|
||||
</el-icon>
|
||||
<div>
|
||||
上传图片
|
||||
</div>
|
||||
|
||||
<!-- <span>{{$t('common.or_drag_the_file_here')}}</span> -->
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
<slot name="image"></slot>
|
||||
|
||||
</div>
|
||||
<div v-if="modelValue" class="up-after" :style="{ width: width, height: height }" v-loading="loading">
|
||||
<img :src="previewImageUrl" />
|
||||
<div class="preview-delete">
|
||||
<el-icon @click="dialogImg(previewImageUrl)">
|
||||
<ZoomIn />
|
||||
</el-icon>
|
||||
<el-icon @click="deleteImg">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="upload-button" v-if="type == 'button'">
|
||||
<el-button class="upload-el-button" type="primary" icon="Upload">
|
||||
<input ref="uploadFileFalod" type="file" @change="getFile" :accept="accept" />
|
||||
点击上传
|
||||
</el-button>
|
||||
<!-- <div class="file-name">{{ fileNmae }}</div> -->
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible" append-to-body width="60%" align-center>
|
||||
<img :src="dialogImageUrl" alt="Preview Image" style="width: 100%" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { uploadFile } from '@/api/upload'
|
||||
import pinia from "@/store/modules/store"
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { ElNotification } from 'element-plus'
|
||||
const store = storeToRefs(pinia())
|
||||
const iconSuffix = store.iconSuffix
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '120px'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '120px'
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
listType: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
|
||||
})
|
||||
const dialogImageUrl = ref('')
|
||||
const imglist = computed(() => {
|
||||
if (props.modelValue) {
|
||||
return props.modelValue.split(',')
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
})
|
||||
const previewImageUrl = computed(() => {
|
||||
return props.modelValue
|
||||
})
|
||||
const loading = ref(false)
|
||||
// 'success'长传成功, 'change'选择文件, 'error'上传失败, 'progress'上传中, 'deleteFile' 删除
|
||||
const emits = defineEmits(["update:modelValue", 'success', 'change', 'error', 'progress', 'deleteFile'])
|
||||
const fileNmae = ref('')
|
||||
const uploadFileImg = ref()
|
||||
const uploadFileFalod = ref()
|
||||
const uploadFileImgs = ref()
|
||||
// const imageUrl = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
const clearFile = (type) => {
|
||||
if (type == 0) {
|
||||
return
|
||||
// uploadFileOne.value.value = ''
|
||||
} else {
|
||||
// uploadFile.value.value = ''
|
||||
}
|
||||
}
|
||||
const getFile = async (e) => {
|
||||
if (e.target === null) {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
const fileMaxSize = 1024 * 1024 * 10
|
||||
const file = e.target.files[0]
|
||||
const size = file.size
|
||||
fileNmae.value = file.name
|
||||
if (size > fileMaxSize) {
|
||||
ElNotification({
|
||||
message: '最大文件不能超过10M',
|
||||
type: 'error',
|
||||
duration: 2000,
|
||||
})
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
emits('change')
|
||||
let formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const imageUrl = URL.createObjectURL(file)
|
||||
if (props.type == 'image') {
|
||||
if (props.listType != 'picture') {
|
||||
// 单个图片
|
||||
emits("update:modelValue", imageUrl)
|
||||
try {
|
||||
const res = await uploadFile(formData, props.action)
|
||||
emits("update:modelValue", res);
|
||||
emits('success', res)
|
||||
loading.value = false
|
||||
clearFile(0)
|
||||
} catch {
|
||||
emits('error')
|
||||
loading.value = false
|
||||
emits("update:modelValue", '');
|
||||
clearFile(0)
|
||||
}
|
||||
} else {
|
||||
// 多个图片
|
||||
imglist.value.push(imageUrl)
|
||||
emits("update:modelValue", imglist.value.join(','))
|
||||
try {
|
||||
const res = await uploadFile(formData, props.action)
|
||||
imglist.value[imglist.value.length - 1] = res.data.url
|
||||
emits("update:modelValue", imglist.value.join(','))
|
||||
emits('success', res)
|
||||
loading.value = false
|
||||
clearFile(1)
|
||||
} catch {
|
||||
imglist.value.splice(imglist.value.length - 1, 1)
|
||||
emits('error')
|
||||
loading.value = false
|
||||
clearFile(1)
|
||||
}
|
||||
}
|
||||
} else if (props.type == 'button') {
|
||||
// 单个文件
|
||||
emits("update:modelValue", imageUrl)
|
||||
let file_size = ''
|
||||
if(size > 1024*1024){
|
||||
file_size = (size / (1024*1024)).toFixed(2) + 'mb'
|
||||
}
|
||||
if(size > 1024 && size < 1024*1024){
|
||||
file_size = (size / 1024).toFixed(2) + 'kb'
|
||||
}
|
||||
if(size < 1024){
|
||||
file_size = size + 'b'
|
||||
}
|
||||
try {
|
||||
const res = await uploadFile(formData, props.action)
|
||||
emits("update:modelValue", props.action + '/' + res.file_url);
|
||||
emits('success', {...res, file_size: file_size})
|
||||
loading.value = false
|
||||
clearFile(0)
|
||||
} catch {
|
||||
emits('error')
|
||||
loading.value = false
|
||||
emits("update:modelValue", '');
|
||||
clearFile(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
const deleteImg = () => {
|
||||
emits('deleteFile')
|
||||
emits("update:modelValue", '');
|
||||
}
|
||||
const deleteImgList = (index) => {
|
||||
imglist.value.splice(index, 1)
|
||||
emits("update:modelValue", imglist.value.join(','))
|
||||
emits('deleteFile')
|
||||
}
|
||||
const dialogImg = (item) => {
|
||||
dialogImageUrl.value = item
|
||||
nextTick(() => {
|
||||
dialogVisible.value = true
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.upload {
|
||||
border-radius: 2px;
|
||||
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.up-front {
|
||||
background: var(--mht-upload-bg-color);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.up-tips {
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
color: var(--el-color-primary);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.plus-icon {
|
||||
font-size: 24px;
|
||||
// margin-top: 20px;
|
||||
}
|
||||
|
||||
// svg {
|
||||
// color: var(--el-color-primary);
|
||||
// width: 40px;
|
||||
// height: 40px;
|
||||
// }
|
||||
|
||||
p {
|
||||
display: block;
|
||||
color: var(--el-color-primary);
|
||||
font-size: 14px;
|
||||
|
||||
.icon {
|
||||
font-size: 18px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--el-text-color-placeholder);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.up-after {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.preview-delete {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.preview-delete {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
color: #fff;
|
||||
|
||||
i {
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
i+i {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-el-button {
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.upload-button{
|
||||
padding-top: 10px;
|
||||
}
|
||||
.file-name {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-secondary)
|
||||
}
|
||||
|
||||
.up-list {
|
||||
overflow: hidden;
|
||||
|
||||
&>div {
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
149
src/components/UserCard/index.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div style="width: 100%;">
|
||||
<el-input v-model="centent" suffix-icon="Search" @input="inputSearch" placeholder="搜索"></el-input>
|
||||
<ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto" :style="'height:' + height" :infinite-scroll-immediate="false" infinite-scroll-distance="5">
|
||||
<li v-for="item in cardlist" :key="item.user_id" class="infinite-list-item" :class="{ active: item.active }"
|
||||
@click="handleItem(item)">
|
||||
<el-image class="user-avatar" :src="item.user_avatar"></el-image>
|
||||
<span class="nickname">{{ item.user_name }}</span>
|
||||
<span class="sex">
|
||||
<!-- <el-icon v-if="item.sex == '男'" style="color: rgb(121.3, 187.1, 255);">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-nan"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
<el-icon v-else style="color: rgb(248, 152.1, 152.1);">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-nv"></use>
|
||||
</svg>
|
||||
</el-icon> -->
|
||||
<!-- <el-icon v-if="item.active" class="check"><Select /></el-icon> -->
|
||||
</span>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
const props = defineProps({
|
||||
cardlist: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
}
|
||||
})
|
||||
const centent = ref('')
|
||||
const emits = defineEmits(["update:modelValue", 'load', 'input', 'change'])
|
||||
const load = () => {
|
||||
emits('load')
|
||||
}
|
||||
|
||||
let timer = null
|
||||
const inputSearch = (e) => {
|
||||
// if (timer != null) {
|
||||
clearTimeout(timer)
|
||||
// timer = null
|
||||
// }
|
||||
timer = setTimeout(() => {
|
||||
emits('input', e)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const handleItem = (row) => {
|
||||
if (props.multiple) {
|
||||
if (!row.active) {
|
||||
row.active = true
|
||||
} else {
|
||||
row.active = false
|
||||
}
|
||||
let arr = []
|
||||
props.cardlist.forEach((el) => {
|
||||
if (el.active === true) {
|
||||
arr.push(el.user_id)
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
emits('change', arr)
|
||||
emits("update:modelValue", arr)
|
||||
})
|
||||
|
||||
} else {
|
||||
// 先全部清空
|
||||
props.cardlist.forEach(el => { el.active = false })
|
||||
// 再设当前为true
|
||||
row.active = true
|
||||
// emit后外部拿到的cardlist状态是唯一active
|
||||
emits('change', row.user_id)
|
||||
emits("update:modelValue", row.user_id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.infinite-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
.infinite-list .infinite-list-item {
|
||||
display: flex;
|
||||
height: 46px;
|
||||
// margin: 10px 0;
|
||||
padding: 0 10px;
|
||||
line-height: 46px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
margin: 0 20px;
|
||||
font-size: 14px ;
|
||||
}
|
||||
|
||||
.sex {
|
||||
.el-icon {
|
||||
font-size: 18px;
|
||||
transform: translateY(5px);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.check {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
color: var(--el-color-primary);
|
||||
top: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: var(--el-color-primary-light-7);
|
||||
}
|
||||
|
||||
.infinite-list .infinite-list-item+.list-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
1145
src/components/UserMessage/index.vue
Normal file
597
src/components/Welcome/index.vue
Normal file
@ -0,0 +1,597 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <label>配置欢迎语</label> -->
|
||||
|
||||
<el-button type="primary" icon="Plus" @click="addhuasu">添加素材</el-button>
|
||||
<div class="meaasge">
|
||||
<el-scrollbar>
|
||||
<ul>
|
||||
<li class="materia-item keyword" v-if="ruleForm.keyword">
|
||||
<el-image class="tobot-image" :src="mihoutai"></el-image>
|
||||
<div class="materia-info">
|
||||
<div class="text">
|
||||
{{ ruleForm.keyword }}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="materia-item" v-for="(item, index) in materiaList" :key="item.id">
|
||||
<el-image class="tobot-image" :src="userRobotStore.robotInfo.head_url"></el-image>
|
||||
<div class="materia-info">
|
||||
<span class="robot-name">{{ userRobotStore.robotInfo.name }}
|
||||
<el-icon class="remove" @click="removeMateria(index)">
|
||||
<Delete />
|
||||
</el-icon></span>
|
||||
|
||||
<div class="text" v-if="item.type == 1">
|
||||
<pre>{{ item.content.message }}</pre>
|
||||
</div>
|
||||
<!-- <div class="text" v-if="item.type == 11">
|
||||
@{{ item.content.meaasge }}
|
||||
|
||||
</div> -->
|
||||
<div class="image" v-if="item.type == 2">
|
||||
<el-image :src="item.content.img_url" fit="cover"
|
||||
:preview-src-list="[item.content.img_url]" preview-teleported></el-image>
|
||||
</div>
|
||||
<div class="gif" v-if="item.type == 8">
|
||||
<el-image :src="item.content.img_url" fit="cover"
|
||||
:preview-src-list="[item.content.img_url]" preview-teleported></el-image>
|
||||
</div>
|
||||
<div class="text audio" v-if="item.type == 3">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yuyin"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
|
||||
{{ item.content.voice_time }} ''
|
||||
</div>
|
||||
<div class="video" v-if="item.type == 4">
|
||||
<video :src="item.content.file_url" controls></video>
|
||||
</div>
|
||||
<div class="text mini" v-if="item.type == 9">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="item.content.userInfo?.avatar"></el-image>
|
||||
<span class="name">{{ item.content.userInfo.nickname
|
||||
}}</span>
|
||||
</div>
|
||||
<p>个人名片</p>
|
||||
|
||||
</div>
|
||||
<div class="text mini chengxu" v-if="item.type == 5">
|
||||
<div class="card-user">
|
||||
<el-image class="avatar" :src="item.content.head_img_url"></el-image>
|
||||
<span class="name">{{ item.content.des }}</span>
|
||||
</div>
|
||||
<span class="cg-text">{{ item.content.title }}</span>
|
||||
<p><el-icon style="color: rgb(109, 109.1, 203.1);">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaochengxu1"></use>
|
||||
</svg>
|
||||
</el-icon>小程序</p>
|
||||
|
||||
</div>
|
||||
<div class="text address" v-if="item.type == 6">
|
||||
<h4 style="margin-bottom: 0;">{{ item.content.address }}</h4>
|
||||
<p style="margin-bottom: 5px;line-height: 18px;">{{
|
||||
item.content.detailed_address }}</p>
|
||||
<el-image class="" :src="addess_png" fit="cover"></el-image>
|
||||
</div>
|
||||
<div class="text link" v-if="item.type == 7">
|
||||
<h4 @click.stop="openLink(item.content.url)">
|
||||
{{ item.content.title }}
|
||||
</h4>
|
||||
<div @click.stop="openLink(item.content.url)">
|
||||
<p>{{ item.content.content }}</p>
|
||||
<el-image class="" :src="item.content.img_url" fit="cover"></el-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text file" v-if="item.type == 10">
|
||||
<div class="file-box">
|
||||
<label>{{ item.content.file_name }}</label>
|
||||
<span>{{ item.content.file_size }}</span>
|
||||
</div>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
<!-- <el-image class="" :src="'item.content.img_url'"
|
||||
fit="cover"></el-image> -->
|
||||
</div>
|
||||
<p class="interval-seconds">发送间隔时间:{{ item.interval_seconds }}s</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div class="btn-box">
|
||||
<el-button type="danger" v-if="welcomeType == 1 && copyMateriaList.length > 0" :class="{ 'no-click': deleteLoading }" @click="deleteReply">
|
||||
<el-icon v-if="deleteLoading" class="mht-loading" style="margin-right: 4px;">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<el-icon v-else style="margin-right: 4px;">
|
||||
<Delete></Delete></el-icon>
|
||||
删除回复语
|
||||
</el-button>
|
||||
<el-button type="primary" :class="{ 'no-click': mhtLoading }" @click="save">
|
||||
<el-icon v-if="mhtLoading" class="mht-loading" style="margin-right: 4px;">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<el-icon v-else style="margin-right: 4px;">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-baocun"></use>
|
||||
</svg></el-icon>
|
||||
保 存
|
||||
</el-button>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible" title="配置素材" width="1000px" align-center destroy-on-close>
|
||||
<div class="dialog-box">
|
||||
|
||||
<UserMessage :isHs="true" @hideMessage="hideMessage"></UserMessage>
|
||||
|
||||
</div>
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { welcomes, addWelcomeMaterial } from '@/api/welcome'
|
||||
import robotStore from '@/store/modules/robotStore'
|
||||
import mihoutai from '@/assets/images/mihoutai.png'
|
||||
import { set_pass_request_reply, pass_request_reply_list, delete_reply } from '@/api/robot'
|
||||
const userRobotStore = robotStore()
|
||||
const ruleFormRef = ref()
|
||||
const ruleForm = ref({})
|
||||
|
||||
const props = defineProps({
|
||||
welcomeType: '0',
|
||||
groupInfo: {},
|
||||
robotInfo: {}
|
||||
})
|
||||
const deleteLoading = ref(false)
|
||||
// let keyword_id = ''
|
||||
const materiaList = ref([])
|
||||
const copyMateriaList = ref([])
|
||||
const getMaterias = async () => {
|
||||
|
||||
if (props.welcomeType == 0) {
|
||||
const res = await welcomes({
|
||||
robot_id: userRobotStore.robotInfo.id,
|
||||
room_id: props.groupInfo.room_id,
|
||||
page: 1,
|
||||
page_size: 100
|
||||
})
|
||||
materiaList.value = res.list.map((el) => {
|
||||
el.content = JSON.parse(el.content)
|
||||
return el
|
||||
})
|
||||
} else {
|
||||
const res = await pass_request_reply_list({
|
||||
robot_id: props.robotInfo.id,
|
||||
page: 1,
|
||||
page_size: 100
|
||||
})
|
||||
materiaList.value = res.list.map((el) => {
|
||||
el.content = JSON.parse(el.content)
|
||||
return el
|
||||
})
|
||||
copyMateriaList.value = JSON.parse(JSON.stringify(materiaList.value))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const addhuasu = () => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const hideMessage = (e) => {
|
||||
dialogVisible.value = false
|
||||
materiaList.value.push(e)
|
||||
}
|
||||
const emits = defineEmits(['hideWelcome'])
|
||||
const save = async () => {
|
||||
|
||||
if (materiaList.value.length == 0) {
|
||||
ElNotification({
|
||||
type: 'warning',
|
||||
message: '请至少添加一条素材'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (props.welcomeType == 0) {
|
||||
const resMaterial = await addWelcomeMaterial({
|
||||
"material_list": materiaList.value.map((el) => {
|
||||
return {
|
||||
content: JSON.stringify(el.content),
|
||||
type: el.type,
|
||||
interval_seconds: el.interval_seconds
|
||||
}
|
||||
}),
|
||||
robot_id: userRobotStore.robotInfo.id,
|
||||
room_id: props.groupInfo.room_id
|
||||
})
|
||||
} else {
|
||||
const resMaterial = await set_pass_request_reply({
|
||||
"material_list": materiaList.value.map((el) => {
|
||||
return {
|
||||
content: JSON.stringify(el.content),
|
||||
type: el.type,
|
||||
interval_seconds: el.interval_seconds
|
||||
}
|
||||
}),
|
||||
robot_id: props.robotInfo.id,
|
||||
})
|
||||
}
|
||||
|
||||
emits('hideWelcome')
|
||||
|
||||
}
|
||||
const deleteReply = async () => {
|
||||
try{
|
||||
deleteLoading.value = true
|
||||
await delete_reply(props.robotInfo.id)
|
||||
emits('hideWelcome')
|
||||
} catch {
|
||||
deleteLoading.value = false
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
const removeMateria = (index) => {
|
||||
materiaList.value.splice(index, 1)
|
||||
}
|
||||
onMounted(() => {
|
||||
getMaterias()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.meaasge {
|
||||
height: 400px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
padding: 25px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.materia-item {
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.tobot-image {
|
||||
float: left;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
width: 300px;
|
||||
float: left;
|
||||
|
||||
.remove {
|
||||
color: var(--el-color-danger);
|
||||
margin-left: 6px;
|
||||
transform: translateY(3px);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.robot-name {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
pre {
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-right: 10px solid #fff;
|
||||
content: '';
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
height: auto;
|
||||
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.gif {
|
||||
.el-image {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.audio {
|
||||
width: 100px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
video {
|
||||
width: 180px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.mini {
|
||||
height: 95px;
|
||||
width: 220px;
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
|
||||
.card-user {
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 15px;
|
||||
margin-left: 5px;
|
||||
transform: translateY(7px);
|
||||
display: inline-block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 20px;
|
||||
line-height: 22px;
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.chengxu {
|
||||
height: 103px;
|
||||
|
||||
.el-image {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.name {
|
||||
transform: translateY(0px);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cg-text {
|
||||
display: block;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
padding: 0;
|
||||
// overflow: hidden;
|
||||
width: 220px;
|
||||
|
||||
h4 {
|
||||
line-height: 28px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
font-weight: normal;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
height: 86px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
width: 220px;
|
||||
height: 100px;
|
||||
|
||||
.el-image {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
float: right;
|
||||
// transform: translateY(-17px);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
// margin-top: 13px;
|
||||
height: 50px;
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--el-text-color-secondary);
|
||||
height: 48px;
|
||||
font-size: 12px;
|
||||
float: left;
|
||||
width: calc(100% - 55px);
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
/* 防止文本换行 */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3; //显示两行文本
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
width: 220px;
|
||||
height: 75px;
|
||||
|
||||
.file-box {
|
||||
float: left;
|
||||
width: calc(100% - 50px);
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
float: right;
|
||||
font-size: 40px;
|
||||
color: var(--el-color-primary);
|
||||
transform: translateY(12px);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
transform: translateY(2px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: normal;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
.interval-seconds {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.keyword {
|
||||
.tobot-image {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.materia-info {
|
||||
text-align: right;
|
||||
float: right;
|
||||
|
||||
.text {
|
||||
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-left: 10px solid #fff;
|
||||
border-right: 0;
|
||||
content: '';
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.btn-box {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
20
src/components/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import SvgIcon from './SvgIcon/index.vue'
|
||||
|
||||
import type { App, Component } from 'vue'
|
||||
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
|
||||
const allGlobalComponent: Component = { SvgIcon }
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
Object.keys(allGlobalComponent).forEach((key: string) => {
|
||||
// 注册为全局组件
|
||||
app.component(key, allGlobalComponent[key])
|
||||
})
|
||||
// 将 element-plus 的图标注册为全局组件
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
},
|
||||
}
|
||||
15
src/directive/has.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// 按钮权限的实现
|
||||
import pinia from '@/store'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
export const isHasButton = (app: any) => {
|
||||
// 自定义指令
|
||||
app.directive('has', {
|
||||
mounted(el: any, options: any) {
|
||||
const userStore = useUserStore(pinia)
|
||||
if (!userStore.buttons.includes(options.value)) {
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
2
src/eventBus/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import mitt from 'mitt'
|
||||
export default mitt()
|
||||
83
src/layout/index.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<script setup>
|
||||
import { onMounted, computed } from 'vue'
|
||||
import Tabbar from './tabbar/index.vue'
|
||||
import Menu from '@/layout/menu/index.vue'
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
const route = useRoute()
|
||||
const router = useRouter();
|
||||
const cachedComponents = computed(() =>
|
||||
router.getRoutes().filter((r) => r.meta.KeepAlive).map((r) => r.name)
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
console.log(cachedComponents.value)
|
||||
window._AMapSecurityConfig = {
|
||||
securityJsCode: "0166bce681642b0e1643544fe0411b9e",
|
||||
};
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="layout_container">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<Tabbar />
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-aside>
|
||||
<Menu />
|
||||
</el-aside>
|
||||
<el-main :class="{ noPadding: route.meta.noPadding }">
|
||||
<router-view v-slot="{ Component }">
|
||||
<Transition name="fade" mode="out-in" enter-from-class="fade-enter">
|
||||
<keep-alive :include="cachedComponents">
|
||||
<component :is="Component" />
|
||||
</keep-alive>
|
||||
<!-- <component v-else :is="Component" /> -->
|
||||
</Transition>
|
||||
</router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-header {
|
||||
padding: 0;
|
||||
height: $navbar-height;
|
||||
}
|
||||
|
||||
.el-aside {
|
||||
width: $base-menu-width;
|
||||
height: calc(100vh - $navbar-height);
|
||||
|
||||
overflow: initial;
|
||||
// overflow-y: hidden;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
height: calc(100vh - $navbar-height);
|
||||
background-color: var(--layout-main-bg-color);
|
||||
// min-width: 1250px;
|
||||
padding: 0;
|
||||
}
|
||||
.noPadding{
|
||||
padding: 0;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
.fade-enter {
|
||||
opacity: 0;
|
||||
transform: translateX(-50px);
|
||||
}
|
||||
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(50px);
|
||||
}
|
||||
</style>
|
||||
56
src/layout/logo/index.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
const goHome = () => {
|
||||
if(userStore.userType != 'system'){
|
||||
router.push('/index')
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="logo">
|
||||
SCRM
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.logo {
|
||||
width: 100%;
|
||||
// height: $base-menu-logo-height;
|
||||
padding: 25px 10px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
// box-shadow: 1px 0 0 rgb(150 150 150 / 20%);
|
||||
.logo-img {
|
||||
width: $base-menu-logo-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
cursor: pointer;
|
||||
img {
|
||||
height: $base-menu-logo-size;
|
||||
width: $base-menu-logo-size;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
}</style>
|
||||
<style lang="scss">
|
||||
.-bosch{
|
||||
.logo{
|
||||
.logo-img{
|
||||
display: none;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
154
src/layout/menu/index.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<script setup>
|
||||
import { onMounted, ref, computed } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const newPath = computed(() => {
|
||||
return route.path
|
||||
})
|
||||
const toLink = (path) => {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
const is_super = computed(() => {
|
||||
return localStorage.is_super
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
|
||||
<el-scrollbar class="menu-scrollbar">
|
||||
<el-menu :default-active="route.path" router>
|
||||
<el-menu-item index="/miniProgram">
|
||||
<el-icon><User /></el-icon>
|
||||
小程序管理
|
||||
</el-menu-item>
|
||||
<el-menu-item v-if="is_super == 1" index="/customerService">
|
||||
<el-icon><Document /></el-icon>
|
||||
客服管理
|
||||
</el-menu-item>
|
||||
|
||||
</el-menu>
|
||||
<!-- <ul>
|
||||
<li @click="toLink('/miniProgram')" :class="{active: newPath == '/miniProgram'}">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zhinengti-F"></use>
|
||||
</svg>
|
||||
|
||||
</el-icon>
|
||||
<span>小程序管理</span>
|
||||
</li> -->
|
||||
<!-- <li @click="toLink('/dashboard')" :class="{active: newPath == '/dashboard'}">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true" style="font-size: 24px">
|
||||
<use xlink:href="#icon-shili"></use>
|
||||
</svg>
|
||||
|
||||
</el-icon>
|
||||
<span>实例</span>
|
||||
</li> -->
|
||||
|
||||
<!-- <li @click="toLink('/user')" :class="{active: newPath == '/user'}">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-yonghu6"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
|
||||
<span>客户</span>
|
||||
</li>
|
||||
<li @click="toLink('/tags')" :class="{active: newPath == '/tags'}">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-biaoqian12"></use>
|
||||
</svg>
|
||||
</el-icon>
|
||||
|
||||
<span>标签组</span>
|
||||
</li>
|
||||
<li @click="toLink('/groups')" :class="{active: newPath == '/groups'}">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-kehu2"></use>
|
||||
</svg>
|
||||
|
||||
</el-icon>
|
||||
<span>群组</span>
|
||||
</li>
|
||||
<li @click="toLink('/materialLibrary')" :class="{active: newPath == '/materialLibrary'}">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-sucaiku2"></use>
|
||||
</svg></el-icon>
|
||||
<span>素材库</span>
|
||||
</li>
|
||||
<li @click="toLink('/agents')" :class="{active: newPath == '/agents'}">
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-AIzhinengti"></use>
|
||||
</svg></el-icon>
|
||||
<span>智能体</span>
|
||||
</li>
|
||||
<li @click="toLink('/task')" :class="{active: newPath == '/task'}">
|
||||
|
||||
<el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-process-operate"></use>
|
||||
</svg></el-icon>
|
||||
<span>SOP</span>
|
||||
</li>
|
||||
|
||||
</ul> -->
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.menu-scrollbar {
|
||||
background-color: #2b3a4a;
|
||||
.el-menu {
|
||||
border-right: none;
|
||||
background-color: #2b3a4a;
|
||||
color: #fff;
|
||||
}
|
||||
.el-menu-item {
|
||||
color: #fff;
|
||||
&:hover {
|
||||
background-color: #34495e;
|
||||
}
|
||||
&.is-active {
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
// box-shadow: var(--el-box-shadow);
|
||||
// overflow: inherit;
|
||||
// li {
|
||||
// height: 65px;
|
||||
// text-align: center;
|
||||
// cursor: pointer;
|
||||
// font-size: 13px;
|
||||
// .el-icon{
|
||||
// font-size: 20px;
|
||||
// margin: 6px;
|
||||
// margin-top: 12px;
|
||||
|
||||
// }
|
||||
// color: var(--el-text-color-regular);
|
||||
// span{
|
||||
// display: block;
|
||||
// text-align: center;
|
||||
// }
|
||||
// &:hover {
|
||||
// background-color: var(--el-color-primary-light-9);
|
||||
// color: var(--el-color-primary);
|
||||
// }
|
||||
// }
|
||||
// .active{
|
||||
// color: var(--el-color-primary);
|
||||
// background-color: var(--el-color-primary-light-8);
|
||||
// }
|
||||
}
|
||||
</style>
|
||||
98
src/layout/tabbar/index.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import Setting from '@/layout/tabbar/setting/index.vue'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const newPath = computed(() => {
|
||||
return route.path
|
||||
})
|
||||
const toLink = (path) => {
|
||||
router.push(path)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tabbar">
|
||||
<div class="left">
|
||||
<!-- <img src="@/assets/images/logo_1.png" /> -->
|
||||
<!-- <el-icon>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-tubiaozhizuomoban-"></use>
|
||||
</svg>
|
||||
</el-icon> -->
|
||||
小程序聊天系统
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="nav">
|
||||
<!-- <ul><li @click="toLink('/dashboard')" :class="{ active: newPath == '/dashboard' }">首页</li></ul> -->
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="setting">
|
||||
<Setting></Setting>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tabbar {
|
||||
width: 100%;
|
||||
height: $navbar-height;
|
||||
line-height: $navbar-height;
|
||||
position: relative;
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
.left {
|
||||
float: left;
|
||||
width: $base-menu-width;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
// img{
|
||||
// width: 30px;
|
||||
// }
|
||||
line-height: $navbar-height;
|
||||
background-color: #2b3a4a;
|
||||
color: #fff;
|
||||
.el-icon{
|
||||
font-size: 36px;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
float: left;
|
||||
padding-left: 10px;
|
||||
width: calc(100% - 300px);
|
||||
.nav{
|
||||
li{
|
||||
display: inline-block;
|
||||
padding: 0 12px;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
// font-weight: bold;
|
||||
&:hover{
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.active{
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.setting{
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
line-height: $navbar-height;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
207
src/layout/tabbar/setting/index.vue
Normal file
@ -0,0 +1,207 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { robotList } from '@/api/robot'
|
||||
// import robotStore from '@/store/modules/robotStore'
|
||||
// const userRobotStore = robotStore()
|
||||
const router = useRouter()
|
||||
const drawer = ref(false)
|
||||
const robot_id = ref('')
|
||||
const robotCards = ref([])
|
||||
const loginOut = () => {
|
||||
drawer.value = false
|
||||
localStorage.removeItem('TOKEN')
|
||||
router.push('/login')
|
||||
}
|
||||
// const robotInfo = computed(() => {
|
||||
// return userRobotStore.robotInfo
|
||||
// })
|
||||
// 全屏功能
|
||||
const fullScreen = () => {
|
||||
// 用来判断是不是全屏返回布尔值
|
||||
const full = document.fullscreenElement
|
||||
// 有兼容问题
|
||||
if (full) {
|
||||
document.exitFullscreen()
|
||||
} else {
|
||||
document.documentElement.requestFullscreen()
|
||||
}
|
||||
}
|
||||
const getRobotList = async () => {
|
||||
robotCards.value = (await robotList({
|
||||
page: 1,
|
||||
page_size: 100
|
||||
})).list
|
||||
|
||||
}
|
||||
onMounted(() => {
|
||||
// robot_id.value = robotInfo.id
|
||||
// getRobotList()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="setting-box">
|
||||
<!-- <span class="robot-name">机器人:{{ userRobotStore.robotInfo.name }}</span> -->
|
||||
<el-icon @click="fullScreen" style="margin-right: 15px;">
|
||||
<FullScreen />
|
||||
</el-icon>
|
||||
<el-icon @click="loginOut" style="color: var(--el-color-danger);">
|
||||
<SwitchButton />
|
||||
</el-icon>
|
||||
<!-- <el-popover placement="bottom" width="200px" trigger="click" offset="30" transition="el-fade-in">
|
||||
<template #reference>
|
||||
<span class="edit-icon" @click.stop>
|
||||
<el-image v-if="!robotInfo?.head_url" class="robot-head" src="@/assets/images/robot.png"></el-image>
|
||||
<el-image v-else class="robot-head" :src="robotInfo.head_url"></el-image>
|
||||
</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="popover-list">
|
||||
<li class="robot">
|
||||
<el-image :src="robotInfo.head_url"></el-image>
|
||||
{{ robotInfo.name }}
|
||||
</li>
|
||||
<li class="delete-icon" @click="loginOut">
|
||||
<el-icon>
|
||||
<SwitchButton />
|
||||
</el-icon>
|
||||
退出登录
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
</el-popover> -->
|
||||
|
||||
</div>
|
||||
<!-- <el-drawer v-model="drawer" title="Settings" size="380px" show-close>
|
||||
<div class="drawer-box">
|
||||
|
||||
<lebel class="select-label">选择您的机器人</lebel>
|
||||
<el-select v-model="robot_id" placeholder="选择机器人" @change="search">
|
||||
<el-option v-for="item in robotCards" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-divider><el-button type="danger" icon="SwitchButton" @click="loginOut">退出登录</el-button></el-divider>
|
||||
</el-drawer> -->
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-box {
|
||||
line-height: $navbar-height;
|
||||
|
||||
:deep(.el-dropdown) {
|
||||
vertical-align: initial;
|
||||
}
|
||||
|
||||
.robot-name {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-regular);
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.robot-head {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
border-radius: 30px;
|
||||
vertical-align: middle;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.popover-list {
|
||||
padding: 10px 0;
|
||||
|
||||
li {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding-left: 15px;
|
||||
|
||||
// border-radius: 6px;
|
||||
// color: var(--el-color-primary);
|
||||
.el-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
// &:hover {
|
||||
// background-color: var(--el-color-primary-light-9);
|
||||
// }
|
||||
}
|
||||
|
||||
.robot {
|
||||
line-height: 40px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 25px;
|
||||
vertical-align: middle;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: var(--el-color-danger);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-drawer__header) {
|
||||
border-bottom: 1px solid var(--el-text-color-regular);
|
||||
|
||||
}
|
||||
|
||||
.drawer-box {
|
||||
line-height: 1;
|
||||
padding: 0 20px;
|
||||
position: relative;
|
||||
|
||||
// top: -40px;
|
||||
.select-label {
|
||||
font-size: 14px;
|
||||
line-height: 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.el-divider {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
width: 91%;
|
||||
|
||||
.el-button {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
72
src/layout/tabbar/setting/theme/index.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { PRE, PRE_DARK, PRE_LIGHT, Levels, WHITE, BLACK } from './type'
|
||||
import { getHexColor } from '@/utils'
|
||||
|
||||
const html = document.documentElement
|
||||
|
||||
/**
|
||||
* 混合颜色
|
||||
*/
|
||||
const mix = (color1: string, color2: string, weight: number) => {
|
||||
weight = Math.max(Math.min(Number(weight), 1), 0)
|
||||
const r1 = parseInt(color1.substring(1, 3), 16)
|
||||
const g1 = parseInt(color1.substring(3, 5), 16)
|
||||
const b1 = parseInt(color1.substring(5, 7), 16)
|
||||
const r2 = parseInt(color2.substring(1, 3), 16)
|
||||
const g2 = parseInt(color2.substring(3, 5), 16)
|
||||
const b2 = parseInt(color2.substring(5, 7), 16)
|
||||
const r = Math.round(r1 * (1 - weight) + r2 * weight)
|
||||
const g = Math.round(g1 * (1 - weight) + g2 * weight)
|
||||
const b = Math.round(b1 * (1 - weight) + b2 * weight)
|
||||
const _r = ('0' + (r || 0).toString(16)).slice(-2)
|
||||
const _g = ('0' + (g || 0).toString(16)).slice(-2)
|
||||
const _b = ('0' + (b || 0).toString(16)).slice(-2)
|
||||
return '#' + _r + _g + _b
|
||||
}
|
||||
|
||||
/**
|
||||
* 更换颜色的方法
|
||||
* @param color 颜色
|
||||
*/
|
||||
const changeTheme = (color?: string | null) => {
|
||||
if (!color) {
|
||||
console.warn('未获取到颜色的值')
|
||||
return
|
||||
}
|
||||
if(!color.includes('#')) {
|
||||
color = getHexColor(color)
|
||||
}
|
||||
// 设置主要颜色变量 --el-color-primary
|
||||
html.style.setProperty(PRE, color)
|
||||
// 循环设置色阶颜色
|
||||
// --el-color-primary-light-${level}
|
||||
Levels.forEach(level => {
|
||||
html.style.setProperty(`${PRE_LIGHT}-${level}`, mix((color as string), WHITE, level * 0.1))
|
||||
})
|
||||
// 设置主要暗色
|
||||
// --el-color-primary-dark-2
|
||||
const dark = mix(color, BLACK, 0.2)
|
||||
html.style.setProperty(`${PRE_DARK}-2`, dark)
|
||||
return color;
|
||||
}
|
||||
|
||||
export function useElementPlusTheme(color: string) {
|
||||
changeTheme(color)
|
||||
// html.style.setProperty('--el-menu-active-color', color)
|
||||
}
|
||||
|
||||
|
||||
let themeData = ''
|
||||
export function darkFontColor(dark: boolean) {
|
||||
if (dark) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 设置不同租户的主题色
|
||||
export const setThemes = (themeInfo: any, isDark: boolean) => {
|
||||
// 主题色
|
||||
|
||||
}
|
||||
14
src/layout/tabbar/setting/theme/type.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/** 变量前缀 */
|
||||
const PRE = '--el-color-primary'
|
||||
/** 浅色变量前缀 */
|
||||
const PRE_LIGHT = `${PRE}-light`
|
||||
/** 深色变量前缀 */
|
||||
const PRE_DARK = `${PRE}-dark`
|
||||
/** 色阶 */
|
||||
const Levels = [3, 5, 7, 8, 9]
|
||||
/** 白色 */
|
||||
const WHITE = '#FFFFFF'
|
||||
/** 黑色 */
|
||||
const BLACK = '#000000'
|
||||
|
||||
export { PRE, PRE_LIGHT, PRE_DARK, Levels, WHITE, BLACK }
|
||||
51
src/main.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
// import '@/styles/element/index.scss'
|
||||
import 'virtual:svg-icons-register'
|
||||
import globalComponent from './components/index'
|
||||
import { router } from './router'
|
||||
import pinia from './store'
|
||||
import 'default-passive-events'
|
||||
import ElementPlus from 'element-plus'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
import 'element-plus/theme-chalk/el-loading.css'
|
||||
import 'element-plus/theme-chalk/el-message.css'
|
||||
import 'element-plus/theme-chalk/el-notification.css'
|
||||
import 'element-plus/theme-chalk/el-message-box.css'
|
||||
import 'element-plus/theme-chalk/el-drawer.css'
|
||||
|
||||
// 暗黑模式
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
|
||||
// 自定义指令
|
||||
import { isHasButton } from '@/directive/has.ts'
|
||||
// import '@/styles/bosch.scss'
|
||||
|
||||
import '@/styles/index.scss'
|
||||
|
||||
|
||||
|
||||
|
||||
import MhtMessageBox from '@/components/MhtMessageBox/index.vue'
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
// 检测到预加载错误,可能是版本更新
|
||||
|
||||
window.addEventListener('vite:preloadError', (event) => {
|
||||
console.log('检测到有新版本,即将刷新...');
|
||||
window.location.reload() // 例如,刷新页面
|
||||
console.log('页面已更新为最新版本...');
|
||||
})
|
||||
// isHasButton(app)
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
})
|
||||
app.use(isHasButton)
|
||||
app.use(globalComponent)
|
||||
app.use('MhtMessageBox', MhtMessageBox)
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
app.mount('#app')
|
||||
221
src/router/index.ts
Normal file
@ -0,0 +1,221 @@
|
||||
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
||||
// import Nprogress from 'nprogress'
|
||||
// import 'nprogress/nprogress.css'
|
||||
import { GET_TOKEN } from '@/utils/token'
|
||||
import { KeepAlive } from 'vue'
|
||||
|
||||
const constantRoutes = [
|
||||
/**
|
||||
* redirect 默认路由:进入项目 默认进入 /index 页面
|
||||
* hidden 是否在路由栏显示
|
||||
* meta : {
|
||||
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
|
||||
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
|
||||
icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg
|
||||
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
|
||||
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
{
|
||||
path: '/login',
|
||||
name: "Login",
|
||||
component: () => import('@/views/login/index.vue'),
|
||||
meta: {
|
||||
title: '登录',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
path: '/',
|
||||
name: 'Layout',
|
||||
component: () => import('@/layout/index.vue'),
|
||||
redirect: '/robot',
|
||||
meta: {
|
||||
title: 'Layout',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'dashboard',
|
||||
component: () => import('@/views/dashboard/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false
|
||||
},
|
||||
},{
|
||||
path: '/groups',
|
||||
name: 'groups',
|
||||
component: () => import('@/views/groups/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: true
|
||||
},
|
||||
},{
|
||||
path: '/groupDetails',
|
||||
name: 'groupDetails',
|
||||
component: () => import('@/views/groups/groupDetails/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
path: '/miniProgram',
|
||||
name: 'miniProgram',
|
||||
component: () => import('@/views/miniProgram/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false,
|
||||
noPadding: true
|
||||
},
|
||||
},{
|
||||
path: '/agents',
|
||||
name: 'agents',
|
||||
component: () => import('@/views/ai/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false,
|
||||
noPadding: true
|
||||
},
|
||||
},{
|
||||
path: '/user',
|
||||
name: 'user',
|
||||
component: () => import('@/views/user/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: true
|
||||
},
|
||||
},{
|
||||
path: '/userDetails',
|
||||
name: 'userDetails',
|
||||
component: () => import('@/views/user/userDetails/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false
|
||||
},
|
||||
},{
|
||||
path: '/task',
|
||||
name: 'task',
|
||||
component: () => import('@/views/task/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false
|
||||
},
|
||||
},{
|
||||
path: '/taskDetails',
|
||||
name: 'taskDetails',
|
||||
component: () => import('@/views/task/taskDetails/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false
|
||||
},
|
||||
},{
|
||||
path: '/materialLibrary',
|
||||
name: 'materialLibrary',
|
||||
component: () => import('@/views/materialLibrary/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false
|
||||
},
|
||||
},{
|
||||
path: '/tags',
|
||||
name: 'tags',
|
||||
component: () => import('@/views/tags/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: true
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/tagChildren',
|
||||
name: 'tagChildren',
|
||||
component: () => import('@/views/tags/tagChildren/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/chat',
|
||||
name: 'chat',
|
||||
component: () => import('@/views/chat/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false,
|
||||
noPadding: true
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/customerService',
|
||||
name: 'customerService',
|
||||
component: () => import('@/views/customerService/index.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
KeepAlive: false,
|
||||
noPadding: true
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)',
|
||||
name: 'Any',
|
||||
component: () => import('@/views/404/index.vue'),
|
||||
meta: {
|
||||
title: '404',
|
||||
hidden: true,
|
||||
icon: '',
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: constantRoutes,
|
||||
// 切换路由跳转到顶部
|
||||
scrollBehavior() {
|
||||
return {
|
||||
left: 0,
|
||||
top: 0
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const whiteList = ['/login']
|
||||
|
||||
// 全局前置守卫
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// Nprogress.start()
|
||||
// document.title = `${setting.title} - ${to.meta.title}`
|
||||
const token = GET_TOKEN()
|
||||
|
||||
|
||||
|
||||
if (token) {
|
||||
if (to.path === '/login') {
|
||||
next('/robot')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
// 没有token的情况下,可以进入白名单
|
||||
if (whiteList.indexOf(to.path) > -1) {
|
||||
next()
|
||||
} else {
|
||||
next('/login')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 全局后置守卫
|
||||
router.afterEach(() => {
|
||||
// Nprogress.done()
|
||||
})
|
||||
|
||||
3
src/setting.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
title: '猕猴桃 Scrm', //项目名称
|
||||
}
|
||||
8
src/store/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// 大仓库
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
export default pinia
|
||||
26
src/store/modules/detailStore.js
Normal file
@ -0,0 +1,26 @@
|
||||
// 详情相关仓库
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
|
||||
const detailStore = defineStore(
|
||||
'detailStore',
|
||||
() => {
|
||||
|
||||
const userDetails = ref({})
|
||||
const groupDetails = ref({})
|
||||
const taskDetails = ref({})
|
||||
|
||||
|
||||
return {
|
||||
userDetails,
|
||||
groupDetails,
|
||||
taskDetails
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: true
|
||||
}
|
||||
)
|
||||
|
||||
export default detailStore
|
||||
24
src/store/modules/robotStore.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// 机器人相关仓库
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
|
||||
const robotStore = defineStore(
|
||||
'newRobot',
|
||||
() => {
|
||||
|
||||
const robotInfo = ref({
|
||||
id: ''
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
robotInfo
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: true
|
||||
}
|
||||
)
|
||||
|
||||
export default robotStore
|
||||
14
src/store/modules/store.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { defineStore } from "pinia"
|
||||
|
||||
const pinia = defineStore('main', {
|
||||
state: () => ({
|
||||
reportName: '', // 报告名称
|
||||
iconSuffix: '',
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
export default pinia
|
||||
3
src/store/modules/types/type.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface UserState {
|
||||
token: string | null
|
||||
}
|
||||
166
src/store/modules/user.ts
Normal file
@ -0,0 +1,166 @@
|
||||
// 用户相关仓库
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { login, boschLogin, singleLogin } from '@/api/login'
|
||||
import { SET_TOKEN, REMOVE_TOKEN } from '@/utils/token'
|
||||
|
||||
const useUserStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
const asyncRoutes = ref()
|
||||
const userType = ref('')
|
||||
const userInfo = ref({
|
||||
enterpriseStyle: {
|
||||
aliasName: ''
|
||||
}
|
||||
})
|
||||
//存储当前用户是否包含某一个按钮
|
||||
const buttons = ref<string[]>([])
|
||||
const version = ref('2.1.1')
|
||||
const classAliasName = ref('')
|
||||
const userLogin = async (data: any) => {
|
||||
//登录请求
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const result: any = await login(data)
|
||||
userInfo.value = result.data
|
||||
SET_TOKEN(result.data.token)
|
||||
btnAuthority(result)
|
||||
getThemeColor(result.data.enterpriseStyle)
|
||||
userType.value = accounstType(result.data)
|
||||
resolve(userType.value)
|
||||
} catch {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
const boschUserLogin = async (data: any) => {
|
||||
//登录请求
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
|
||||
const result: any = await boschLogin(data)
|
||||
userInfo.value = result.data
|
||||
SET_TOKEN(result.data.token)
|
||||
btnAuthority(result)
|
||||
getThemeColor(result.data.enterpriseStyle)
|
||||
userType.value = accounstType(result.data)
|
||||
resolve(userType.value)
|
||||
|
||||
} catch {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
const singleLoginStore = async (data: any) => {
|
||||
//公用单点登录请求
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const result: any = await singleLogin(data)
|
||||
userInfo.value = result.data
|
||||
SET_TOKEN(result.data.token)
|
||||
btnAuthority(result)
|
||||
getThemeColor(result.data.enterpriseStyle)
|
||||
userType.value = accounstType(result.data)
|
||||
resolve(userType.value)
|
||||
} catch {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
const getThemeColor = async (res) => {
|
||||
|
||||
if (res) {
|
||||
userInfo.value.enterpriseStyle = res
|
||||
if(userInfo.value.enterpriseStyle.aliasName == 'bosch'){
|
||||
classAliasName.value = '-bosch'
|
||||
} else {
|
||||
classAliasName.value = ''
|
||||
}
|
||||
} else {
|
||||
classAliasName.value = ''
|
||||
userInfo.value.enterpriseStyle = {
|
||||
aliasName: '',
|
||||
errorTipColor: '#f56c6c',
|
||||
extraFileUrl: '',
|
||||
fontColor: '#6C6C6E',
|
||||
fontFileUrl: '',
|
||||
infoTipColor: '#909399',
|
||||
logoUrl: '',
|
||||
middleFontColor: '#909399',
|
||||
smallFontColor: '#A8ABB2',
|
||||
successTipColor: '#67C23A',
|
||||
themeColor: '#5CC4A7',
|
||||
warnTipColor: '#E6A23C',
|
||||
borderColor: '#ebeef5'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const userLogout = async () => {
|
||||
//退出登录请求
|
||||
REMOVE_TOKEN()
|
||||
}
|
||||
|
||||
|
||||
// 按钮权限
|
||||
const btnAuthority = (result) => {
|
||||
buttons.value = []
|
||||
if (result.data.purviewResponse.rbacResourceActions != null && result.data.purviewResponse.rbacResourceActions != 'null') {
|
||||
result.data.purviewResponse.rbacResourceActions.forEach(el => {
|
||||
buttons.value.push(el.identifying)
|
||||
});
|
||||
}
|
||||
}
|
||||
// 确定账号类型
|
||||
const accounstType = (res) => {
|
||||
// let userType = ''
|
||||
// 是否包含系统管理员
|
||||
const type = res.purviewResponse.roles.some((el) => {
|
||||
return el.id == import.meta.env.VITE_APP_ROLES_MAIN
|
||||
})
|
||||
if (!type) {
|
||||
// 是否包含企业管理员
|
||||
const type2 = res.purviewResponse.roles.some((el) => {
|
||||
return el.id == import.meta.env.VITE_APP_ROLES_QY
|
||||
})
|
||||
if (!type2) {
|
||||
// 是否包含普通用户
|
||||
const type3 = res.purviewResponse.roles.some((el) => {
|
||||
return el.id == import.meta.env.VITE_APP_ROLES_PT
|
||||
})
|
||||
if (type3) {
|
||||
return 'user'
|
||||
}
|
||||
} else {
|
||||
return 'company'
|
||||
}
|
||||
} else {
|
||||
return 'system'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
userType,
|
||||
userInfo,
|
||||
asyncRoutes,
|
||||
buttons,
|
||||
version,
|
||||
classAliasName,
|
||||
userLogin,
|
||||
userLogout,
|
||||
getThemeColor,
|
||||
boschUserLogin,
|
||||
singleLoginStore
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: true
|
||||
}
|
||||
)
|
||||
|
||||
export default useUserStore
|
||||
118
src/styles/element/index.scss
Normal file
@ -0,0 +1,118 @@
|
||||
// styles/element/index.scss
|
||||
/* 只需要重写你需要的即可 */
|
||||
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
|
||||
$colors: (
|
||||
'primary': (
|
||||
'base': #5CC4A7,
|
||||
),
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
// 如果只是按需导入,则可以忽略以下内容。
|
||||
// 如果你想导入所有样式:
|
||||
// @use "element-plus/theme-chalk/src/index.scss" as *;
|
||||
// 自定义输入框、选择器等样式
|
||||
|
||||
// .el-input__wrapper {
|
||||
// background: rgba(249, 249, 249, 0.80);
|
||||
// box-shadow: none;
|
||||
// &:hover{
|
||||
// box-shadow: none!important;
|
||||
// }
|
||||
// }
|
||||
// .el-input__wrapper.is-focus{
|
||||
// box-shadow: none;
|
||||
// }
|
||||
// .el-input__wrapper:hover{
|
||||
// box-shadow: none;
|
||||
// }
|
||||
// .el-input-group__append {
|
||||
// background: rgba(249, 249, 249, 0.80);
|
||||
// box-shadow: none;
|
||||
// padding: 0 15px 0 0px;
|
||||
// font-size: 12px;
|
||||
// }
|
||||
// .el-input-group__prepend{
|
||||
// background: rgba(249, 249, 249, 0.80);
|
||||
// box-shadow: none;
|
||||
// padding: 0 0 0 15px;
|
||||
// font-size: 12px;
|
||||
// }
|
||||
|
||||
// .el-textarea__inner {
|
||||
// background: rgba(249, 249, 249, 0.80);
|
||||
// box-shadow: none;
|
||||
// }
|
||||
|
||||
// .is-focus {
|
||||
// box-shadow: none;
|
||||
// border: 0;
|
||||
// }
|
||||
|
||||
// .el-cascader{
|
||||
// width: 100%;
|
||||
// }
|
||||
// .el-cascader .el-input.is-focus .el-input__wrapper{
|
||||
// box-shadow: none!important;
|
||||
// }
|
||||
// .el-select {
|
||||
|
||||
// .el-input.is-focus{
|
||||
// .el-input__wrapper{
|
||||
// box-shadow: none!important;
|
||||
|
||||
// }
|
||||
// }
|
||||
// .el-input__wrapper.is-focus{
|
||||
|
||||
// box-shadow: none!important;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// .el-date-editor.el-input__wrapper{
|
||||
// box-shadow: none;
|
||||
// background-color: #F9F9F9;
|
||||
// width: 260px;
|
||||
// }
|
||||
// .el-range-editor.is-active{
|
||||
// box-shadow: none!important;
|
||||
// }
|
||||
// .el-range-editor.is-active:hover{
|
||||
// box-shadow: none;
|
||||
// }
|
||||
// .el-date-editor.el-input__wrapper{
|
||||
// box-shadow: none!important;
|
||||
// }
|
||||
// .el-table th.el-table__cell.is-leaf{
|
||||
// // color: #5CC4A7;
|
||||
// color: #4E5969;
|
||||
// font-weight: 400;
|
||||
// background-color: #ebf9f6;
|
||||
// }
|
||||
// .el-dialog__body{
|
||||
// padding-top: 0;
|
||||
// }
|
||||
// .el-dialog{
|
||||
// border-radius: 18px;
|
||||
|
||||
// }
|
||||
|
||||
// .el-form-item{
|
||||
// display: block;
|
||||
|
||||
// }
|
||||
// .el-form-item__label{
|
||||
// font-size: 14px;
|
||||
// color: #4E5969;
|
||||
// }
|
||||
// .op{
|
||||
// .el-form-item__label{
|
||||
// opacity: 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .el-cascader-node__prefix{
|
||||
// position: absolute !important;
|
||||
// }
|
||||
431
src/styles/index.scss
Normal file
@ -0,0 +1,431 @@
|
||||
//引入清除默认样式
|
||||
@import './reset.scss';
|
||||
|
||||
//滚动条外观设置
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
width: 10px;
|
||||
background-color: #c4cbd7;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:root {
|
||||
--el-border-radius-base: 0 !important;
|
||||
--layout-main-bg-color: rgb(247 250 252) !important;
|
||||
--el-fill-color-blank: rgba(240, 245, 245, 1) !important;
|
||||
--mht-upload-bg-color: rgba(240, 245, 245, 1);
|
||||
}
|
||||
.el-radio{
|
||||
--el-radio-input-bg-color: #fff!important;
|
||||
}
|
||||
.el-message-box {
|
||||
--el-messagebox-border-radius: 0;
|
||||
}
|
||||
|
||||
/** 暗黑主题 */
|
||||
html.dark {
|
||||
--layout-main-bg-color: rgb(18 18 18);
|
||||
}
|
||||
|
||||
.el-input {
|
||||
--el-select-input-focus-border-color: none !important;
|
||||
--el-input-bg-color: rgba(240, 245, 245, 1) !important;
|
||||
--el-input-focus-border-color: none;
|
||||
--el-input-border-color: none !important;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
--el-table-header-text-color: var(--el-color-primary) !important;
|
||||
--el-table-bg-color: var(--el-bg-color) !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// .el-select__wrapper {
|
||||
// background-color: var(--el-input-bg-color) !important;
|
||||
// }
|
||||
|
||||
.el-input,
|
||||
.el-textarea,
|
||||
input {
|
||||
--el-input-text-color: var(--input-text-color) !important;
|
||||
}
|
||||
|
||||
|
||||
.el-input__wrapper.is-focus,
|
||||
.el-select__wrapper.is-focused,
|
||||
.el-select__wrapper {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.el-textarea,
|
||||
.el-date-editor,
|
||||
.el-cascader {
|
||||
--el-input-focus-border-color: none !important;
|
||||
--el-input-bg-color: rgba(240, 245, 245, 1) !important;
|
||||
--el-input-border-color: none !important;
|
||||
}
|
||||
|
||||
.el-cascader {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
padding: 12px 16px !important;
|
||||
}
|
||||
|
||||
.el-input__wrapper:hover,
|
||||
.el-textarea__inner:hover,
|
||||
.el-input-group__append:hover,
|
||||
.el-input-group__prepend:hover {
|
||||
box-shadow: none !important;
|
||||
|
||||
}
|
||||
|
||||
.el-input__wrapper.is-focus,
|
||||
.el-textarea__inner:focus,
|
||||
.el-date-editor.is-active {
|
||||
box-shadow: none !important;
|
||||
|
||||
}
|
||||
|
||||
.el-input-group__append,
|
||||
.el-input-group__prepend {
|
||||
background: var(--el-input-bg-color) !important;
|
||||
box-shadow: none !important;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
|
||||
|
||||
.el-textarea,
|
||||
.el-date-editor,
|
||||
.el-cascader {
|
||||
--el-input-focus-border-color: none !important;
|
||||
--el-input-border-color: none !important;
|
||||
}
|
||||
|
||||
.el-cascader {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.el-dialog__body {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
border-radius: var(--el-border-radius-boundary) !important;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.el-overlay {
|
||||
font-family: var(--el-overlay-font);
|
||||
}
|
||||
|
||||
.el-table--default .el-table__cell {
|
||||
padding: 12px 0 !important;
|
||||
}
|
||||
|
||||
|
||||
// 下拉框选中的颜色
|
||||
// .el-select-dropdown__item.is-selected {
|
||||
// color: #fff !important;
|
||||
// background-color: var(--el-color-primary) !important;
|
||||
// }
|
||||
|
||||
.el-select-dropdown__list {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
// .el-select-dropdown{
|
||||
// border-radius: var(--el-border-radius-base)!important;
|
||||
// }
|
||||
.el-popper {
|
||||
border-radius: var(--el-border-radius-base) !important;
|
||||
margin-top: -10px !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.el-popper__arrow {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.el-select-dropdown__item {
|
||||
// height: 38px !important;
|
||||
// line-height: 38px !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.el-dialog__header {
|
||||
padding-top: 12px !important;
|
||||
padding-bottom: 12px !important;
|
||||
margin-right: 0 !important;
|
||||
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
top: 2px !important;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.el-form-item.is-error .el-input__wrapper,
|
||||
.el-form-item.is-error .el-select-v2__wrapper,
|
||||
.el-form-item.is-error .el-select-v2__wrapper:focus,
|
||||
.el-form-item.is-error .el-textarea__inner,
|
||||
.el-form-item.is-error .el-textarea__inner:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.el-cascader__dropdown.el-popper,
|
||||
.el-picker__popper.el-popper,
|
||||
.el-popover.el-popper,
|
||||
.el-notification,
|
||||
.el-select__popper.el-popper {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
// .el-date-table td.in-range .el-date-table-cell {
|
||||
// color: #fff !important;
|
||||
// }
|
||||
|
||||
.el-cascader-node.is-active {
|
||||
color: #fff !important;
|
||||
background-color: var(--el-color-primary) !important;
|
||||
}
|
||||
|
||||
// .el-cascader-menu__list{
|
||||
// li[aria-haspopup="true"]{
|
||||
// background-color: var(--el-color-primary)!important;
|
||||
// }
|
||||
// }
|
||||
.el-step__description {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.el-form-item {
|
||||
display: block !important;
|
||||
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
font-size: .14rem;
|
||||
color: #4E5969;
|
||||
}
|
||||
|
||||
.op {
|
||||
.el-form-item__label {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-cascader-node__prefix {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
span[class=el-tooltip__trigger] {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
padding-left: 15px;
|
||||
// font-weight: bold;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
border: 0 !important;
|
||||
--el-card-border-radius: 0 !important;
|
||||
--el-card-bg-color: var(--el-bg-color) !important;
|
||||
}
|
||||
|
||||
|
||||
.dialog-box {
|
||||
padding: 30px;
|
||||
padding-top: 10px;
|
||||
|
||||
.dialog-label {
|
||||
display: block;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.dialog-p {
|
||||
font-size: 14px;
|
||||
margin-top: 20px;
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
}
|
||||
|
||||
.amap-sug-result {
|
||||
z-index: 999999;
|
||||
border: 0;
|
||||
box-shadow: var(--el-box-shadow);
|
||||
|
||||
.auto-item {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
--el-checkbox-bg-color: var(--el-card-bg-color) !important;
|
||||
}
|
||||
|
||||
.el-card__body {
|
||||
height: 100%;
|
||||
}
|
||||
.operations {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.operations .el-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.operations .primary:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.mht-operations{
|
||||
.el-icon + .el-icon{
|
||||
margin-left: 6px;
|
||||
}
|
||||
.el-icon {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
transform: translateY(4px);
|
||||
&:hover{
|
||||
color: var(--el-color-primary-light-3);
|
||||
}
|
||||
}
|
||||
.el-icon-danger{
|
||||
color: var(--el-color-danger);
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
color: var(--el-color-danger-light-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.pagination-box {
|
||||
overflow: hidden;
|
||||
margin-top: 25px;
|
||||
text-align: right;
|
||||
height: 30px;
|
||||
|
||||
.el-pagination {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
.no-click {
|
||||
pointer-events: none;
|
||||
}
|
||||
.mht-loading {
|
||||
transform: rotate(45deg);
|
||||
animation: spin 2s linear infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.el-button{
|
||||
line-height: normal;
|
||||
}
|
||||
.el-table .row-success{
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
.el-table .row-danger{
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
|
||||
.phone-list{
|
||||
li{
|
||||
line-height: 26px;
|
||||
padding-left: 15px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.el-tour__footer{
|
||||
display: none!important;
|
||||
}
|
||||
.el-tour__body{
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
|
||||
.costom-select-item {
|
||||
|
||||
font-size: 14px;
|
||||
|
||||
.el-image {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 8px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.customClass {
|
||||
.el-message-box__content {
|
||||
padding: 20px 20px;
|
||||
}
|
||||
|
||||
.el-message-box__btns {
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-footer{
|
||||
padding-right: 30px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.up_head_img{
|
||||
.upload-button{
|
||||
padding-top: 0!important;
|
||||
}
|
||||
}
|
||||
198
src/styles/reset.scss
Normal file
@ -0,0 +1,198 @@
|
||||
*,
|
||||
*:after,
|
||||
*:before {
|
||||
box-sizing: border-box;
|
||||
|
||||
outline: none;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
span,
|
||||
applet,
|
||||
object,
|
||||
iframe,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
a,
|
||||
abbr,
|
||||
acronym,
|
||||
address,
|
||||
big,
|
||||
cite,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
em,
|
||||
img,
|
||||
ins,
|
||||
kbd,
|
||||
q,
|
||||
s,
|
||||
samp,
|
||||
small,
|
||||
strike,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
tt,
|
||||
var,
|
||||
b,
|
||||
u,
|
||||
i,
|
||||
center,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
article,
|
||||
aside,
|
||||
canvas,
|
||||
details,
|
||||
embed,
|
||||
figure,
|
||||
figcaption,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
output,
|
||||
ruby,
|
||||
section,
|
||||
summary,
|
||||
time,
|
||||
mark,
|
||||
audio,
|
||||
video {
|
||||
// font: inherit;
|
||||
font-family: "Helvetica Neue";
|
||||
font-size: 100%;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
vertical-align: baseline;
|
||||
|
||||
border: 0;
|
||||
}
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
font-size: 16px;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote,
|
||||
q {
|
||||
quotes: none;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
|
||||
position: relative;
|
||||
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
button {
|
||||
// font-family: inhert;
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: inherit;
|
||||
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
select {
|
||||
text-indent: .01px;
|
||||
text-overflow: '';
|
||||
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
select::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// code,
|
||||
// pre {
|
||||
// font-family: monospace, monospace;
|
||||
// font-size: 1em;
|
||||
// }
|
||||
|
||||
|
||||
.svg-icon{
|
||||
fill: currentColor;
|
||||
}
|
||||
html.dark{
|
||||
--el-text-color-regular: #606266;
|
||||
}
|
||||
12
src/styles/variable.scss
Normal file
@ -0,0 +1,12 @@
|
||||
//项目提供scss全局变量
|
||||
|
||||
|
||||
//左侧的菜单的宽度
|
||||
$base-menu-width:200px;
|
||||
// 顶部导航栏高度
|
||||
$navbar-height: 60px;
|
||||
|
||||
//左侧菜单的背景颜色
|
||||
$base-menu-background:#111112;
|
||||
|
||||
|
||||
53
src/utils/echarts/index.ts
Normal file
@ -0,0 +1,53 @@
|
||||
// * Echarts 按需引入
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
BarChart,
|
||||
// 系列类型的定义后缀都为 SeriesOption
|
||||
BarSeriesOption,
|
||||
LineChart,
|
||||
LineSeriesOption
|
||||
} from "echarts/charts";
|
||||
import { LegendComponent } from "echarts/components";
|
||||
import {
|
||||
TitleComponent,
|
||||
// 组件类型的定义后缀都为 ComponentOption
|
||||
TitleComponentOption,
|
||||
TooltipComponent,
|
||||
TooltipComponentOption,
|
||||
GridComponent,
|
||||
GridComponentOption,
|
||||
// 数据集组件
|
||||
DatasetComponent,
|
||||
DatasetComponentOption,
|
||||
// 内置数据转换器组件 (filter, sort)
|
||||
TransformComponent
|
||||
} from "echarts/components";
|
||||
import { LabelLayout, UniversalTransition } from "echarts/features";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
|
||||
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
|
||||
export type ECOption = echarts.ComposeOption<
|
||||
| BarSeriesOption
|
||||
| LineSeriesOption
|
||||
| TitleComponentOption
|
||||
| TooltipComponentOption
|
||||
| GridComponentOption
|
||||
| DatasetComponentOption
|
||||
>;
|
||||
|
||||
// 注册必须的组件
|
||||
echarts.use([
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
BarChart,
|
||||
LineChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer
|
||||
]);
|
||||
|
||||
export default echarts;
|
||||
55
src/utils/index.ts
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @description 生成随机数
|
||||
* @param {Number} min 最小值
|
||||
* @param {Number} max 最大值
|
||||
* @returns {Number}
|
||||
*/
|
||||
export function randomNum(min: number, max: number): number {
|
||||
const num = Math.floor(Math.random() * (min - max) + max)
|
||||
return num
|
||||
}
|
||||
|
||||
/**
|
||||
* rgba转#
|
||||
* @param color 颜色
|
||||
*/
|
||||
export function getHexColor(color: string) {
|
||||
const values = color
|
||||
.replace(/rgba?\(/, '')
|
||||
.replace(/\)/, '')
|
||||
.replace(/[\s+]/g, '')
|
||||
.split(',')
|
||||
const a = parseFloat(values[3]),
|
||||
r = Math.floor(a * parseInt(values[0]) + (1 - a) * 255),
|
||||
g = Math.floor(a * parseInt(values[1]) + (1 - a) * 255),
|
||||
b = Math.floor(a * parseInt(values[2]) + (1 - a) * 255)
|
||||
return '#' +
|
||||
('0' + r.toString(16)).slice(-2) +
|
||||
('0' + g.toString(16)).slice(-2) +
|
||||
('0' + b.toString(16)).slice(-2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Component的方法
|
||||
* @param view 路径
|
||||
* @param type 路由层数 目前只兼容二级路由
|
||||
*/
|
||||
export function _getViews(view: any, type: any) {
|
||||
let res;
|
||||
let modules: any;
|
||||
if (type == "one") {
|
||||
modules = import.meta.glob("/src/view/*.vue");
|
||||
} else {
|
||||
modules = import.meta.glob("/src/view/**/*.vue");
|
||||
}
|
||||
for (const path in modules) {
|
||||
const dir =
|
||||
type == "one"
|
||||
? path.split("view/")[1].split(".vue")[0]
|
||||
: path.split("view/")[1].split(".vue")[0];
|
||||
if (dir === view) {
|
||||
res = () => modules[path]();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
66
src/utils/request.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import axios from 'axios'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { GET_TOKEN, REMOVE_TOKEN } from '@/utils/token'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import pinia from '@/store'
|
||||
//创建axios实例
|
||||
const request = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
timeout: 600000,
|
||||
})
|
||||
// 存储需要取消的请求
|
||||
const requestsToCancel = {};
|
||||
|
||||
//请求拦截器
|
||||
request.interceptors.request.use((config: any) => {
|
||||
if (GET_TOKEN()) {
|
||||
config.headers.Authorization = GET_TOKEN()
|
||||
}
|
||||
// 为每个请求生成一个唯一的cancel token
|
||||
config.cancelToken = new axios.CancelToken(cancel => {
|
||||
requestsToCancel[config.url] = cancel;
|
||||
});
|
||||
return config
|
||||
})
|
||||
//响应拦截器
|
||||
request.interceptors.response.use(
|
||||
(response: any) => {
|
||||
return response.data
|
||||
|
||||
},
|
||||
(error: any) => {
|
||||
//处理网络错误
|
||||
if (error.response?.data.code == 20107) {
|
||||
return
|
||||
}
|
||||
// if (error.response?.data.code == 10101) {
|
||||
// Object.values(requestsToCancel).forEach(cancel => cancel());
|
||||
// }
|
||||
if (error.response?.data.code == 10103) {
|
||||
Object.values(requestsToCancel).forEach(cancel => cancel());
|
||||
const useUser = useUserStore(pinia)
|
||||
useUser.userInfo = {}
|
||||
useUser.token = ''
|
||||
REMOVE_TOKEN()
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: error.response.data.message,
|
||||
duration: 2000,
|
||||
onClose: () => {
|
||||
location.href = '/index'
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: error.response.data.message,
|
||||
duration: 2000,
|
||||
})
|
||||
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
export default request
|
||||
12
src/utils/time.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export const getTime = () => {
|
||||
const hours = new Date().getHours()
|
||||
if (hours <= 9) {
|
||||
return '早上好 🌅'
|
||||
} else if (hours <= 12) {
|
||||
return '上午好 🌞'
|
||||
} else if (hours <= 18) {
|
||||
return '下午好 ☕️'
|
||||
} else {
|
||||
return '晚上好 🌛'
|
||||
}
|
||||
}
|
||||
18
src/utils/token.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export const SET_TOKEN = (token: string) => {
|
||||
localStorage.setItem('TOKEN', token)
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const GET_TOKEN = () => {
|
||||
return localStorage.getItem('TOKEN')
|
||||
}
|
||||
|
||||
export const REMOVE_TOKEN = () => {
|
||||
localStorage.removeItem('TOKEN')
|
||||
}
|
||||
export const SET_USER_TYPE = (type) => {
|
||||
localStorage.removeItem('userType')
|
||||
}
|
||||
|
||||
|
||||