xiaohuo 10 months ago
commit 6e7cbe2585
  1. 261
      ALOps_sys_fe/alops-ui/src/components/Qa/index.vue
  2. 728
      ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index.vue
  3. 272
      ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index2.vue
  4. 264
      ALOps_sys_fe/alops-ui/src/views/index.vue
  5. 752
      ALOps_sys_sql/aiops_sys.sql

@ -0,0 +1,261 @@
<template>
<div id="app">
<el-card class="customer-service">
<!-- 客服头部 -->
<div class="service-header">
<h2>智能客服</h2>
<p>您好我是智能助手很高兴为您服务</p>
</div>
<div class="service-container">
<!-- 左侧常见问题 -->
<div class="faq-section">
<h3>常见问题</h3>
<div
v-for="(faq, index) in faqs"
:key="index"
class="faq-item"
@click="selectFaq(faq)"
>
{{ faq.question }}
</div>
</div>
<!-- 右侧聊天区域 -->
<div class="chat-section">
<div class="chat-messages" ref="messagesContainer">
<div
v-for="(message, index) in messages"
:key="index"
:class="['message', message.sender]"
>
<div class="message-content">
<div>{{ message.text }}</div>
<div class="timestamp">{{ message.time }}</div>
</div>
</div>
</div>
<div class="chat-input">
<el-input
v-model="newMessage"
placeholder="请输入您的问题..."
@keyup.enter.native="sendMessage"
></el-input>
<el-button
type="primary"
@click="sendMessage"
:disabled="!newMessage.trim()"
>发送</el-button>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script>
import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/inMonitoring/assessment"
import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
export default {
name: "User",
dicts: ['sys_normal_disable', 'sys_user_sex'],
components: { Treeselect, Splitpanes, Pane },
data() {
return {
newMessage: '',
messages: [
{
text: '您好!我是智能客服,请问有什么可以帮您?',
sender: 'service',
time: this.formatTime(new Date())
}
],
faqs: [
{ question: '如何查询订单状态?', answer: '您可以在"我的订单"页面查看订单的当前状态和物流信息。' },
{ question: '如何办理退货?', answer: '请在订单详情页点击"申请退货",填写相关信息后提交申请。' },
{ question: '支付方式有哪些?', answer: '我们支持支付宝、微信支付、银联等多种支付方式。' },
{ question: '会员有哪些优惠?', answer: '会员可享受购物折扣、专属优惠券、生日特权等多重优惠。' },
{ question: '商品何时发货?', answer: '一般情况下,我们会在订单支付成功后24小时内安排发货。' }
]
};
},mounted() {
//
this.scrollToBottom();
},
methods: {
sendMessage() {
if (!this.newMessage.trim()) return;
//
this.messages.push({
text: this.newMessage,
sender: 'customer',
time: this.formatTime(new Date())
});
//
setTimeout(() => {
this.autoReply(this.newMessage);
}, 1000);
this.newMessage = '';
},
autoReply(userMessage) {
let replyText = '';
//
if (userMessage.includes('订单') || userMessage.includes('物流')) {
replyText = '您可以在"我的订单"页面查看订单详情和物流信息。';
} else if (userMessage.includes('支付') || userMessage.includes('付款')) {
replyText = '我们支持支付宝、微信支付、银联等多种支付方式。';
} else if (userMessage.includes('退货') || userMessage.includes('退款')) {
replyText = '如需退货退款,请在订单详情页提交申请,客服会尽快处理。';
} else if (userMessage.includes('会员') || userMessage.includes('优惠')) {
replyText = '会员可享受专属折扣和优惠券,详情请查看会员中心。';
} else if (userMessage.includes('发票')) {
replyText = '您可以在下单时选择开具发票,我们提供电子发票和纸质发票两种方式。';
} else {
replyText = '抱歉,我没有完全理解您的问题。您可以尝试联系人工客服获得更详细的帮助。';
}
//
this.messages.push({
text: replyText,
sender: 'service',
time: this.formatTime(new Date())
});
//
this.scrollToBottom();
},
selectFaq(faq) {
//
this.messages.push({
text: faq.question,
sender: 'customer',
time: this.formatTime(new Date())
});
//
setTimeout(() => {
this.messages.push({
text: faq.answer,
sender: 'service',
time: this.formatTime(new Date())
});
this.scrollToBottom();
}, 1000);
},
scrollToBottom() {
this.$nextTick(() => {
const container = this.$refs.messagesContainer;
container.scrollTop = container.scrollHeight;
});
},
formatTime(date) {
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
},
watch: {
messages: {
handler() {
this.scrollToBottom();
},
deep: true
}
}
}
</script>
<style scoped>
#app {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 20px;
}
.customer-service {
max-width: 1000px;
margin: 0 auto;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.service-header {
background-color: #409EFF;
color: white;
padding: 15px 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.service-container {
display: flex;
height: 500px;
}
.faq-section {
width: 30%;
border-right: 1px solid #e6e6e6;
overflow-y: auto;
padding: 15px;
}
.chat-section {
width: 70%;
display: flex;
flex-direction: column;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 15px;
}
.message {
margin-bottom: 15px;
display: flex;
}
.message.customer {
justify-content: flex-end;
}
.message-content {
max-width: 70%;
padding: 10px 15px;
border-radius: 4px;
}
.message.customer .message-content {
background-color: #ecf5ff;
border: 1px solid #d9ecff;
}
.message.service .message-content {
background-color: #f4f4f5;
border: 1px solid #e9e9eb;
}
.chat-input {
padding: 15px;
border-top: 1px solid #e6e6e6;
display: flex;
}
.chat-input .el-input {
margin-right: 10px;
}
.faq-item {
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid #ebeef5;
}
.faq-item:hover {
background-color: #f5f7fa;
}
.timestamp {
font-size: 12px;
color: #909399;
margin-top: 5px;
text-align: right;
}
</style>

@ -1,174 +1,62 @@
<template> <template>
<div class="app-container">
<el-row :gutter="20">
<splitpanes :horizontal="this.$store.getters.device === 'mobile'" class="default-theme">
<!--类型类型管理-->
<pane size="84">
<el-col>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="类型名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入类型名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="类型状态" clearable style="width: 240px">
<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8"> <div id="app">
<el-col :span="1.5"> <el-card class="customer-service">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['device:deviceType:add']">新增</el-button> <!-- 客服头部 -->
</el-col> <div class="service-header">
<el-col :span="1.5"> <h2>智能客服</h2>
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['device:deviceType:edit']">修改</el-button> <p>您好我是智能助手很高兴为您服务</p>
</el-col> </div>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['device:deviceType:remove']">删除</el-button> <div class="service-container">
</el-col> <!-- 左侧常见问题 -->
<el-col :span="1.5"> <div class="faq-section">
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['device:deviceType:import']">导入</el-button> <h3>常见问题</h3>
</el-col> <div
<el-col :span="1.5"> v-for="(faq, index) in faqs"
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['device:deviceType:export']">导出</el-button> :key="index"
</el-col> class="faq-item"
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar> @click="selectFaq(faq)"
</el-row> >
{{ faq.question }}
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange"> </div>
<el-table-column type="selection" width="50" align="center" /> </div>
<el-table-column label="类型编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
<el-table-column label="类型名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" /> <!-- 右侧聊天区域 -->
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible"> <div class="chat-section">
<template slot-scope="scope"> <div class="chat-messages" ref="messagesContainer">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> <div
</template> v-for="(message, index) in messages"
</el-table-column> :key="index"
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160"> :class="['message', message.sender]"
<template slot-scope="scope"> >
<span>{{ parseTime(scope.row.createTime) }}</span> <div class="message-content">
</template> <div>{{ message.text }}</div>
</el-table-column> <div class="timestamp">{{ message.time }}</div>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width"> </div>
<template slot-scope="scope" v-if="scope.row.userId !== 1"> </div>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['device:deviceType:edit']">修改</el-button> </div>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:deviceType:remove']">删除</el-button>
</template> <div class="chat-input">
</el-table-column> <el-input
</el-table> v-model="newMessage"
placeholder="请输入您的问题..."
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> @keyup.enter.native="sendMessage"
</el-col> ></el-input>
</pane> <el-button
</splitpanes> type="primary"
</el-row> @click="sendMessage"
:disabled="!newMessage.trim()"
<el-dialog :title="title" :visible.sync="open" width="760px" append-to-body> >发送</el-button>
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
<el-row :gutter="20">
<!-- 类型昵称 + 手机号码 -->
<el-col :span="12">
<el-form-item label="类型昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入类型昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<!-- 邮箱 + 类型名称 -->
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="类型名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入类型名称" maxlength="30" />
</el-form-item>
</el-col>
<!-- 类型密码仅新增+ 类型性别 -->
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="类型密码" prop="password">
<el-input v-model="form.password" placeholder="请输入类型密码" type="password" maxlength="20" show-password />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="类型性别">
<el-select v-model="form.sex" placeholder="请选择性别" style="width: 100%">
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<!-- 状态 + 角色 -->
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="角色">
<el-select v-model="form.roleIds" multiple placeholder="请选择角色" style="width: 100%">
<el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" :disabled="item.status == 1"></el-option>
</el-select>
</el-form-item>
</el-col>
<!-- 备注单独一行 -->
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</span>
</el-dialog>
<!-- 类型导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的类型数据
</div> </div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div> </div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div> </div>
</el-dialog> </el-card>
</div> </div>
</template> </template>
<script> <script>
import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/system/user" import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/inMonitoring/assessment"
import { getToken } from "@/utils/auth" import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect" import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css" import "@riophae/vue-treeselect/dist/vue-treeselect.css"
@ -181,341 +69,193 @@ export default {
components: { Treeselect, Splitpanes, Pane }, components: { Treeselect, Splitpanes, Pane },
data() { data() {
return { return {
// newMessage: '',
loading: true, messages: [
// {
ids: [], text: '您好!我是智能客服,请问有什么可以帮您?',
// sender: 'service',
single: true, time: this.formatTime(new Date())
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
userList: null,
//
title: "",
//
deptOptions: undefined,
//
enabledDeptOptions: undefined,
//
open: false,
//
deptName: undefined,
//
initPassword: undefined,
//
dateRange: [],
//
postOptions: [],
//
roleOptions: [],
//
form: {},
defaultProps: {
children: "children",
label: "label"
},
//
upload: {
//
open: false,
//
title: "",
//
isUploading: false,
//
updateSupport: 0,
//
headers: { Authorization: "Bearer " + getToken() },
//
url: process.env.VUE_APP_BASE_API + "/system/user/importData"
},
//
queryParams: {
pageNum: 1,
pageSize: 10,
userName: undefined,
phonenumber: undefined,
status: undefined,
deptId: undefined
},
//
columns: [
{ key: 0, label: '类型编号', visible: true, showInSelector: true },
{ key: 1, label: '类型名称', visible: true, showInSelector: true },
{ key: 2, label: '类型昵称', visible: true, showInSelector: true },
{ key: 3, label: '部门', visible: false, showInSelector: false }, // 👈
{ key: 4, label: '手机号码', visible: true, showInSelector: true },
{ key: 5, label: '状态', visible: true, showInSelector: true },
{ key: 6, label: '创建时间', visible: true, showInSelector: true }
],
//
rules: {
userName: [
{ required: true, message: "类型名称不能为空", trigger: "blur" },
{ min: 2, max: 20, message: '类型名称长度必须介于 2 和 20 之间', trigger: 'blur' }
],
nickName: [
{ required: true, message: "类型昵称不能为空", trigger: "blur" }
],
password: [
{ required: true, message: "类型密码不能为空", trigger: "blur" },
{ min: 5, max: 20, message: '类型密码长度必须介于 5 和 20 之间', trigger: 'blur' },
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
],
email: [
{
type: "email",
message: "请输入正确的邮箱地址",
trigger: ["blur", "change"]
}
],
phonenumber: [
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur"
}
]
}
}
},
watch: {
//
deptName(val) {
this.$refs.tree.filter(val)
} }
}, ],
created() { faqs: [
this.getList() { question: '如何查询订单状态?', answer: '您可以在"我的订单"页面查看订单的当前状态和物流信息。' },
// this.getDeptTree() { question: '如何办理退货?', answer: '请在订单详情页点击"申请退货",填写相关信息后提交申请。' },
this.getConfigKey("sys.user.initPassword").then(response => { { question: '支付方式有哪些?', answer: '我们支持支付宝、微信支付、银联等多种支付方式。' },
this.initPassword = response.msg { question: '会员有哪些优惠?', answer: '会员可享受购物折扣、专属优惠券、生日特权等多重优惠。' },
}) { question: '商品何时发货?', answer: '一般情况下,我们会在订单支付成功后24小时内安排发货。' }
}, ]
methods: { };
/** 查询类型列表 */ },mounted() {
getList() { //
this.loading = true this.scrollToBottom();
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => { },
this.userList = response.rows methods: {
this.total = response.total sendMessage() {
this.loading = false if (!this.newMessage.trim()) return;
}
) //
}, this.messages.push({
// /** */ text: this.newMessage,
// getDeptTree() { sender: 'customer',
// deptTreeSelect().then(response => { time: this.formatTime(new Date())
// this.deptOptions = response.data });
// this.enabledDeptOptions = this.filterDisabledDept(JSON.parse(JSON.stringify(response.data)))
// }) //
// }, setTimeout(() => {
// this.autoReply(this.newMessage);
filterDisabledDept(deptList) { }, 1000);
return deptList.filter(dept => {
if (dept.disabled) { this.newMessage = '';
return false
}
if (dept.children && dept.children.length) {
dept.children = this.filterDisabledDept(dept.children)
}
return true
})
},
//
filterNode(value, data) {
if (!value) return true
return data.label.indexOf(value) !== -1
},
//
handleNodeClick(data) {
this.queryParams.deptId = data.id
this.handleQuery()
},
//
handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用"
this.$modal.confirm('确认要"' + text + '""' + row.userName + '"类型吗?').then(function() {
return changeUserStatus(row.userId, row.status)
}).then(() => {
this.$modal.msgSuccess(text + "成功")
}).catch(function() {
row.status = row.status === "0" ? "1" : "0"
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
userId: undefined,
deptId: undefined,
userName: undefined,
nickName: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: "0",
remark: undefined,
postIds: [],
roleIds: []
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = []
this.resetForm("queryForm")
this.queryParams.deptId = undefined
this.$refs.tree.setCurrentKey(null)
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.userId)
this.single = selection.length != 1
this.multiple = !selection.length
},
//
handleCommand(command, row) {
switch (command) {
case "handleResetPwd":
this.handleResetPwd(row)
break
case "handleAuthRole":
this.handleAuthRole(row)
break
default:
break
}
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
getUser().then(response => {
this.postOptions = response.posts
this.roleOptions = response.roles
this.open = true
this.title = "添加类型"
this.form.password = this.initPassword
})
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const userId = row.userId || this.ids
getUser(userId).then(response => {
this.form = response.data
this.postOptions = response.posts
this.roleOptions = response.roles
this.$set(this.form, "postIds", response.postIds)
this.$set(this.form, "roleIds", response.roleIds)
this.open = true
this.title = "修改类型"
this.form.password = ""
})
},
/** 重置密码按钮操作 */
handleResetPwd(row) {
this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: "类型密码长度必须介于 5 和 20 之间",
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return "不能包含非法字符:< > \" ' \\\ |"
}
}, },
}).then(({ value }) => { autoReply(userMessage) {
resetUserPwd(row.userId, value).then(response => { let replyText = '';
this.$modal.msgSuccess("修改成功,新密码是:" + value)
}) //
}).catch(() => {}) if (userMessage.includes('订单') || userMessage.includes('物流')) {
}, replyText = '您可以在"我的订单"页面查看订单详情和物流信息。';
/** 分配角色操作 */ } else if (userMessage.includes('支付') || userMessage.includes('付款')) {
handleAuthRole: function(row) { replyText = '我们支持支付宝、微信支付、银联等多种支付方式。';
const userId = row.userId } else if (userMessage.includes('退货') || userMessage.includes('退款')) {
this.$router.push("/system/user-auth/role/" + userId) replyText = '如需退货退款,请在订单详情页提交申请,客服会尽快处理。';
}, } else if (userMessage.includes('会员') || userMessage.includes('优惠')) {
/** 提交按钮 */ replyText = '会员可享受专属折扣和优惠券,详情请查看会员中心。';
submitForm: function() { } else if (userMessage.includes('发票')) {
this.$refs["form"].validate(valid => { replyText = '您可以在下单时选择开具发票,我们提供电子发票和纸质发票两种方式。';
if (valid) {
if (this.form.userId != undefined) {
updateUser(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else { } else {
addUser(this.form).then(response => { replyText = '抱歉,我没有完全理解您的问题。您可以尝试联系人工客服获得更详细的帮助。';
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
} }
//
this.messages.push({
text: replyText,
sender: 'service',
time: this.formatTime(new Date())
});
//
this.scrollToBottom();
},
selectFaq(faq) {
//
this.messages.push({
text: faq.question,
sender: 'customer',
time: this.formatTime(new Date())
});
//
setTimeout(() => {
this.messages.push({
text: faq.answer,
sender: 'service',
time: this.formatTime(new Date())
});
this.scrollToBottom();
}, 1000);
},
scrollToBottom() {
this.$nextTick(() => {
const container = this.$refs.messagesContainer;
container.scrollTop = container.scrollHeight;
});
},
formatTime(date) {
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
} }
}) },
}, watch: {
/** 删除按钮操作 */ messages: {
handleDelete(row) { handler() {
const userIds = row.userId || this.ids this.scrollToBottom();
this.$modal.confirm('是否确认删除类型编号为"' + userIds + '"的数据项?').then(function() { },
return delUser(userIds) deep: true
}).then(() => { }
this.getList() }
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('system/user/export', {
...this.queryParams
}, `user_${new Date().getTime()}.xlsx`)
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = "类型导入"
this.upload.open = true
},
/** 下载模板操作 */
importTemplate() {
this.download('system/user/importTemplate', {
}, `user_template_${new Date().getTime()}.xlsx`)
},
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false
this.upload.isUploading = false
this.$refs.upload.clearFiles()
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true })
this.getList()
},
//
submitFileForm() {
this.$refs.upload.submit()
}
}
} }
</script> </script>
<style scoped>
#app {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 20px;
}
.customer-service {
/* max-width: 1000px; */
margin: 0 auto;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.service-header {
background-color: #409EFF;
color: white;
padding: 15px 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.service-container {
display: flex;
min-height: 400px;
}
.faq-section {
width: 30%;
border-right: 1px solid #e6e6e6;
overflow-y: auto;
padding: 15px;
}
.chat-section {
width: 70%;
display: flex;
flex-direction: column;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 15px;
}
.message {
margin-bottom: 15px;
display: flex;
}
.message.customer {
justify-content: flex-end;
}
.message-content {
max-width: 70%;
padding: 10px 15px;
border-radius: 4px;
}
.message.customer .message-content {
background-color: #ecf5ff;
border: 1px solid #d9ecff;
}
.message.service .message-content {
background-color: #f4f4f5;
border: 1px solid #e9e9eb;
}
.chat-input {
padding: 15px;
border-top: 1px solid #e6e6e6;
display: flex;
}
.chat-input .el-input {
margin-right: 10px;
}
.faq-item {
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid #ebeef5;
}
.faq-item:hover {
background-color: #f5f7fa;
}
.timestamp {
font-size: 12px;
color: #909399;
margin-top: 5px;
text-align: right;
}
</style>

@ -0,0 +1,272 @@
<template>
<div class="chat-container">
<!-- 消息列表区域 -->
<el-scrollbar
ref="messageScroll"
class="message-area"
@scroll.native="handleScroll">
<div
v-for="(msg, index) in messages"
:key="index"
:class="['message', msg.sender === 'user' ? 'user-msg' : 'customer-msg']">
<el-avatar :size="40" :src="msg.avatar"></el-avatar>
<div class="message-content">
<div class="sender">{{ msg.sender === 'user' ? '我' : '客服' }}</div>
<div class="content">{{ msg.content }}</div>
<div class="time">{{ formatTime(msg.timestamp) }}</div>
</div>
</div>
<!-- 加载中提示 -->
<div
v-if="loading"
class="loading-indicator">
<i class="el-icon-loading"></i> 加载中...
</div>
</el-scrollbar>
<!-- 输入区域 -->
<div class="input-area">
<el-input
v-model="inputMessage"
type="textarea"
placeholder="请输入消息..."
@keyup.enter.native="sendMessage"
:disabled="!connected">
</el-input>
<div class="action-buttons">
<el-button
type="primary"
@click="sendMessage"
:disabled="!connected || !inputMessage.trim()"
:loading="sending">
发送
</el-button>
<el-button @click="toggleConnection">
{{ connected ? '断开' : '连接' }}
</el-button>
</div>
</div>
</div>
</template>
<script>
import { Loading } from 'element-ui';
export default {
data() {
return {
messages: [],
inputMessage: '',
connected: false,
loading: false,
sending: false,
page: 1,
pageSize: 20
};
},
mounted() {
this.connect();
this.scrollToBottom();
},
methods: {
//
connect() {
this.connected = true;
this.loadMessages();
},
//
toggleConnection() {
this.connected = !this.connected;
if (this.connected) {
this.loadMessages();
}
},
//
async loadMessages() {
this.loading = true;
try {
// API
const response = await this.mockApiCall();
this.messages = response.data;
this.page++;
} catch (error) {
console.error('加载消息失败:', error);
} finally {
this.loading = false;
this.$nextTick(this.scrollToBottom);
}
},
//
sendMessage() {
if (!this.inputMessage.trim()) return;
this.sending = true;
//
setTimeout(() => {
this.messages.push({
sender: 'user',
content: this.inputMessage,
timestamp: Date.now(),
avatar: 'https://via.placeholder.com/40?text=U'
});
this.inputMessage = '';
this.sending = false;
this.scrollToBottom();
//
this.simulateCustomerReply();
}, 500);
},
//
simulateCustomerReply() {
setTimeout(() => {
this.messages.push({
sender: 'customer',
content: '您好,已收到您的消息,我们会尽快处理',
timestamp: Date.now(),
avatar: 'https://via.placeholder.com/40?text=C'
});
this.scrollToBottom();
}, 1000);
},
//
scrollToBottom() {
const scrollContainer = this.$refs.messageScroll.$el.querySelector('.el-scrollbar__wrap');
if (scrollContainer) {
scrollContainer.scrollTop = scrollContainer.scrollHeight;
}
},
//
handleScroll(e) {
const { scrollTop, scrollHeight, clientHeight } = e.target;
const scrollBottom = scrollHeight - scrollTop - clientHeight;
//
if (scrollBottom < 50 && !this.loading) {
this.loadMoreMessages();
}
},
//
async loadMoreMessages() {
this.loading = true;
try {
// API
const response = await this.mockApiCall(this.page);
this.messages = [...response.data, ...this.messages];
this.page++;
} catch (error) {
console.error('加载更多消息失败:', error);
} finally {
this.loading = false;
}
},
//
formatTime(timestamp) {
return new Date(timestamp).toLocaleTimeString();
},
// API
mockApiCall(page = 1) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: Array(this.pageSize).fill().map((_, i) => ({
sender: Math.random() > 0.5 ? 'user' : 'customer',
content: `消息 ${(page-1)*this.pageSize + i + 1}`,
timestamp: Date.now() - i * 1000,
avatar: Math.random() > 0.5
? 'https://via.placeholder.com/40?text=U'
: 'https://via.placeholder.com/40?text=C'
}))
});
}, 800);
});
}
}
};
</script>
<style>
.chat-container {
display: flex;
flex-direction: column;
height: 100vh;
border: 1px solid #ebeef5;
border-radius: 4px;
}
.message-area {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.message {
display: flex;
margin-bottom: 20px;
}
.user-msg {
flex-direction: row-reverse;
}
.customer-msg {
flex-direction: row;
}
.message-content {
margin-left: 15px;
background: #f0f0f0;
padding: 12px;
border-radius: 8px;
max-width: 80%;
}
.user-msg .message-content {
background: #e1f0ff;
}
.sender {
font-weight: bold;
margin-bottom: 5px;
}
.content {
white-space: pre-wrap;
}
.time {
font-size: 0.8em;
color: #999;
text-align: right;
margin-top: 5px;
}
.input-area {
padding: 15px;
border-top: 1px solid #ebeef5;
background: #fff;
}
.action-buttons {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.loading-indicator {
text-align: center;
padding: 15px;
color: #999;
}
</style>

@ -1,105 +1,187 @@
<template> <template>
<div class="dashboard-container"> <div class="dashboard">
<div class="welcome-message"> <h1>唐山烟草智能网络设备运维与问答系统</h1>
<h1>欢迎进入中国烟草</h1>
<p>这里是中国烟草的主界面您可以在这里进行各种操作和管理</p> <el-row :gutter="20">
</div> <!-- 设备状态监控 -->
<el-col :span="12">
<el-card>
<h2>设备状态监控</h2>
<!-- 这里可以添加设备状态监控的具体内容例如表格或图表 -->
<el-table :data="deviceStatusData" style="width: 100%">
<el-table-column prop="name" label="设备名称"></el-table-column>
<el-table-column prop="location" label="设备位置"></el-table-column>
<el-table-column prop="cpuUsage" label="CPU使用率"></el-table-column>
<el-table-column prop="memoryUsage" label="内存使用率"></el-table-column>
<el-table-column prop="status" label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '正常' ? 'success' : 'danger'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<!-- 设备状态统计图表 -->
<el-col :span="12">
<el-card>
<h2>检测设备信息 (按状态统计饼状图)</h2>
<!-- 这里可以使用echarts或其他图表库来展示饼状图 -->
<div id="statusChart" style="width: 100%; height: 300px;"></div>
</el-card>
<el-card>
<h2>检测设备信息 (按设备类型饼状图)</h2>
<div id="typeChart" style="width: 100%; height: 300px;"></div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 20px;">
<!-- 设备预警信息 -->
<el-col :span="12">
<el-card>
<h2>设备预警信息</h2>
<el-table :data="alertData" style="width: 100%">
<el-table-column prop="time" label="预警时间"></el-table-column>
<el-table-column prop="device" label="设备名称"></el-table-column>
<el-table-column prop="location" label="设备位置"></el-table-column>
<el-table-column prop="level" label="预警等级">
<template slot-scope="scope">
<el-tag :type="getTagType(scope.row.level)">
{{ scope.row.level }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<!-- 预警信息统计 -->
<el-col :span="12">
<el-card>
<h2>预警信息统计</h2>
<div id="alertStatsChart" style="width: 100%; height: 300px;"></div>
</el-card>
</el-col>
</el-row>
</div> </div>
</template><style scoped> </template>
.dashboard-container {
padding: 20px;
text-align: center;
}
.welcome-message {
margin-top: 50px;
}
.welcome-message h1 {
font-size: 36px;
color: #333;
margin-bottom: 20px;
}
.welcome-message p { <script>
font-size: 18px; import * as echarts from 'echarts'
color: #666; require('echarts/theme/macarons') // echarts theme
max-width: 800px;
margin: 0 auto;
}
</style><style scoped>
.dashboard-container {
padding: 20px;
text-align: center;
background-color: #f8f9fa; /* 背景颜色 */
/* 或者使用背景图片 */
/* background-image: url('background.jpg');
background-size: cover;
background-position: center; */
}
.welcome-message { export default {
margin-top: 50px; data() {
padding: 20px; return {
border-radius: 8px; deviceStatusData: [
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); //
background-color: #fff; /* 欢迎信息背景颜色 */ { name: '设备1', location: '位置1', cpuUsage: '30%', memoryUsage: '40%', status: '正常' },
} { name: '设备2', location: '位置2', cpuUsage: '70%', memoryUsage: '80%', status: '异常' },
],
alertData: [
//
{ time: '2023-10-01 10:00:00', device: '设备2', location: '位置2', level: '高' },
{ time: '2023-10-01 11:00:00', device: '设备3', location: '位置3', level: '中' },
],
};
},
mounted() {
this.initCharts();
},
methods: {
getTagType(level) {
const types = { '高': 'danger', '中': 'warning', '低': 'success' };
return types[level] || 'info';
},
initCharts() {
//
const statusChart = echarts.init(document.getElementById('statusChart'));
statusChart.setOption({
title: { text: '设备状态统计', subtext: '按状态', left: 'center' },
tooltip: { trigger: 'item' },
series: [
{
name: '设备状态',
type: 'pie',
radius: '50%',
data: [
{ value: 44, name: '自主房屋' },
{ value: 33, name: '出租房屋' },
{ value: 11, name: '空置房屋' },
{ value: 11, name: '其他' },
],
},
],
});
.welcome-message h1 { //
font-size: 36px; const typeChart = echarts.init(document.getElementById('typeChart'));
color: #333; typeChart.setOption({
margin-bottom: 20px; title: { text: '设备类型统计', subtext: '按设备类型', left: 'center' },
} tooltip: { trigger: 'item' },
series: [
{
name: '设备类型',
type: 'pie',
radius: '50%',
data: [
{ value: 44, name: '自主房屋' },
{ value: 33, name: '出租房屋' },
{ value: 11, name: '空置房屋' },
{ value: 11, name: '其他' },
],
},
],
});
.welcome-message p { //
font-size: 18px; const alertStatsChart = echarts.init(document.getElementById('alertStatsChart'));
color: #666; alertStatsChart.setOption({
max-width: 800px; title: { text: '预警信息统计', left: 'center' },
margin: 0 auto; tooltip: {},
} radar: {
</style><template> indicator: [
<div class="dashboard-container"> { name: '火警类', max: 500 },
<div class="welcome-message"> { name: '冲禁类', max: 500 },
<h1><i class="el-icon-s-operation"></i> 欢迎进入中国烟草</h1> { name: '灾害', max: 500 },
<p>这里是中国烟草的主界面您可以在这里进行各种操作和管理</p> { name: '群体性', max: 500 },
</div> { name: '行政类', max: 500 },
</div> { name: '交通类', max: 500 },
</template> { name: '重大', max: 500 },
],
},
series: [{
name: '预警统计',
type: 'radar',
data: [
{
value: [75, 452, 64, 94, 44, 0, 29],
name: '预警次数',
},
],
}],
});
},
},
};
</script>
<style scoped> <style>
.dashboard-container { .dashboard {
padding: 20px; padding: 20px;
text-align: center; background-color: #f0f2f5;
min-height: 100vh;
} }
.welcome-message { h1, h2 {
margin-top: 50px; text-align: center;
animation: fadeIn 1s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
} }
.welcome-message h1 { .el-card {
font-size: 36px;
color: #333;
margin-bottom: 20px; margin-bottom: 20px;
} }
</style>
.welcome-message p {
font-size: 18px;
color: #666;
max-width: 800px;
margin: 0 auto;
}
</style>

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save