首页更新

This commit is contained in:
hrx 2026-07-09 16:18:13 +08:00
parent 89f384e858
commit 6138987986
6 changed files with 1159 additions and 76 deletions

View File

@ -163,6 +163,25 @@ export function reqApproveUserSearch(data){
})
}
// 咨询表单状态切换
export function reqUserInquiryStatusSwitch(data){
return request({
url: '/product/update_user_inquiry.dspy',
method: 'get',
headers: { 'Content-Type': 'application/json' },
params: data
})
}
// 咨询表单删除(批量 单个)
export function reqUserInquiryDelete(data){
return request({
url: '/product/delete_user_inquiry.dspy',
method: 'get',
headers: { 'Content-Type': 'application/json' },
params: data
})
}
//政企审核 更新 /user/enterprise_audit_info_update.dspy
export function reqEnterpriseUpdate(data){

View File

@ -1,7 +1,7 @@
<template>
<div class="box">
<div>
<div class="contener" v-if=" orgType == 2 || orgType == 3 || !userid">
<div class="contener" v-if="shouldShowFloatingConsult">
<transition name="slide">
@ -79,6 +79,7 @@ import axios from "axios";
import {mapState} from "vuex";
import {getCustomerOfSaleUserId} from "@/api/customer/vedio";
import { getHomePath } from "@/views/setting/tools";
import { reqNewHomeConsult } from "@/api/newHome";
export default {
//
@ -149,7 +150,16 @@ export default {
// console.log("~~~",this.$route)
},
computed: {},
computed: {
shouldShowFloatingConsult() {
const isHomePage = [
'/homePage/index',
'/homePage/indexLast',
'/ncmatchHome/index'
].includes(this.$route.path)
return isHomePage || this.orgType == 2 || this.orgType == 3 || !this.userid
}
},
methods: {
// getHomePath()
getHomePath,
@ -245,7 +255,376 @@ export default {
this.isShowChangeChat = true
},
openAiConsult() {
window.dispatchEvent(new CustomEvent('kboss-open-ai-chat'))
this.resetYunbaoChatInstance()
this.installYunbaoChat()
window.YunbaoChat.init({
avatarUrl: require('./img/ocai.jpg'),
name: '云宝小助手'
})
window.YunbaoChat.open()
},
resetYunbaoChatInstance() {
const oldModal = document.getElementById('yunbaoModal')
const oldStyle = document.getElementById('yunbao-chat-css')
if (oldModal) oldModal.remove()
if (oldStyle) oldStyle.remove()
window.YunbaoChat = null
},
installYunbaoChat() {
if (window.YunbaoChat && document.getElementById('yunbaoModal')) return
if (window.YunbaoChat && !document.getElementById('yunbaoModal')) {
window.YunbaoChat = null
}
const vm = this
window.YunbaoChat = {
_inited: false,
_chatInited: false,
_selectedProduct: '',
_config: {},
init(config = {}) {
if (this._inited) return
this._inited = true
this._config = Object.assign({
avatarUrl: '',
name: '云宝小助手',
submitUrl: '',
products: [
{ key: 'E投标', intro: '适配全品类电子招投标场景的线上投标工具,支持在线完成信息注册、标书获取、文件编制、加密提交全流程操作。实时同步招标变更与澄清信息,保障投标过程安全可追溯。' },
{ key: 'E招标', intro: '全流程电子化招标交易核心工具支持多品类招标业务线上开展实现招投标全过程无纸化运行。搭载多CA互认、在线清标、电子保函等实用功能保障招标合规高效。' },
{ key: 'E评标', intro: '智能化线上评审系统,覆盖专家签到、清标比对、符合性评审、打分汇总、报告生成全流程。支持标书交叉比对、废标在线操作与澄清收发,提升评标效率与规范性。' },
{ key: '采伐智审', intro: '面向林业管理的行业智能体,可在线完成采伐申请材料的智能核验,自动比对采伐范围、树种、蓄积量等核心指标与合规要求,快速识别违规申请,助力林业资源可持续利用。' },
{ key: '燃机智慧监盘', intro: '能源领域工业智能体实时采集燃机运行的温度、压力、振动等多维度数据通过AI算法识别异常运行征兆提前预判潜在故障保障燃机稳定运行降低运维成本。' },
{ key: '电价预测', intro: '面向能源行业的预测智能体,结合历史电价数据、供需变化、政策调整、天气影响等多维度变量,通过机器学习模型实现不同周期的电价精准预测,帮助电力企业优化收益管理。' },
{ key: 'AI漫剧制作', intro: '互联网内容创作智能体覆盖从剧本生成、分镜拆解、素材生成到视频合成的全流程制作。支持工业化批量生产帮助中小团队低成本快速产出AI漫画作品大幅降低创作门槛。' },
{ key: '合同智能审查', intro: 'AI驱动的办公智能体可快速识别合同中的风险条款、表述歧义、合规漏洞自动标注问题位置并给出修改建议。支持多类型合同模板适配大幅缩短人工审查时长。' },
{ key: '投策智能体', intro: '面向企业投资决策的辅助智能体,整合多维度市场数据、行业趋势与政策信息,通过算法模拟不同决策场景的收益与风险,自动生成可视化分析报告,提升投资方案科学性。' }
],
pages: {
'合同智能审查': '/homePage/agentStore/contractCase',
'投策智能体': '/homePage/agentStore/decisionCase'
}
}, config)
this.injectCSS()
this.injectHTML()
this.bindBaseEvents()
},
open() {
const modal = document.getElementById('yunbaoModal')
if (!modal) return
modal.querySelectorAll('.yb-chat-avatar img,.yb-chat-msg-avatar img').forEach((img) => {
img.src = this._config.avatarUrl
})
modal.classList.add('active')
if (!this._chatInited) {
this._chatInited = true
this.initChat()
}
},
close() {
const modal = document.getElementById('yunbaoModal')
if (modal) modal.classList.remove('active')
},
injectCSS() {
if (document.getElementById('yunbao-chat-css')) return
const style = document.createElement('style')
style.id = 'yunbao-chat-css'
style.textContent = vm.getYunbaoChatCSS()
document.head.appendChild(style)
},
injectHTML() {
if (document.getElementById('yunbaoModal')) return
const div = document.createElement('div')
div.innerHTML = vm.getYunbaoChatHTML(this._config)
document.body.appendChild(div.firstElementChild)
},
bindBaseEvents() {
const modal = document.getElementById('yunbaoModal')
const closeBtn = document.getElementById('yunbaoCloseBtn')
if (modal) {
modal.addEventListener('click', (event) => {
if (event.target === modal) this.close()
})
}
if (closeBtn) closeBtn.addEventListener('click', () => this.close())
},
initChat() {
const body = document.getElementById('yunbaoChatBody')
if (!body) return
body.innerHTML = ''
setTimeout(() => {
this.addBotMsg('嗨~我是云宝 👋<br>请问您想随便看看,还是直接聊聊需求?', [
{ text: '随便看看', action: 'browse' },
{ text: '直接聊聊需求', action: 'direct' }
])
}, 200)
},
addBotMsg(html, options = [], displayType = '') {
const body = document.getElementById('yunbaoChatBody')
if (!body) return
const msg = document.createElement('div')
msg.className = 'yb-chat-msg'
const type = displayType || (options[0] && options[0].type) || ''
let optionHtml = ''
if (options.length) {
if (type === 'cards') {
optionHtml = `<div class="yb-chat-product-cards">${options.map(item => `
<div class="yb-chat-product-card" data-action="${item.action || ''}" data-param="${item.param || ''}" data-url="${item.url || ''}" data-text="${item.text || ''}">
<div class="yb-chat-product-card-title">${item.text}</div>
<div class="yb-chat-product-card-desc">${item.desc || ''}</div>
<span class="yb-chat-product-card-link">查看更多 </span>
</div>
`).join('')}</div>`
} else if (type === 'tags') {
optionHtml = `<div class="yb-chat-tag-options">${options.map(item => `<button class="yb-chat-tag-btn" data-action="${item.action || ''}" data-param="${item.param || ''}" data-text="${item.text || ''}">${item.text}</button>`).join('')}</div>`
} else {
optionHtml = `<div class="yb-chat-options">${options.map(item => `<button class="${item.primary ? 'yb-chat-option-btn primary' : 'yb-chat-option-btn'}" data-action="${item.action || ''}" data-param="${item.param || ''}" data-text="${item.text || ''}">${item.text}</button>`).join('')}</div>`
}
}
msg.innerHTML = `
<div class="yb-chat-msg-avatar"><img src="${this._config.avatarUrl}" alt="云宝"></div>
<div>
<div class="yb-chat-msg-bubble">${html}</div>
${optionHtml}
</div>
`
body.appendChild(msg)
body.scrollTop = body.scrollHeight
msg.querySelectorAll('[data-action]').forEach(btn => {
btn.addEventListener('click', () => this.handleAction(btn.getAttribute('data-action'), btn.getAttribute('data-param'), btn.getAttribute('data-text') || btn.innerText.trim(), btn.getAttribute('data-url')))
})
},
addUserMsg(text) {
const body = document.getElementById('yunbaoChatBody')
if (!body) return
const msg = document.createElement('div')
msg.className = 'yb-chat-msg yb-chat-msg-user'
msg.innerHTML = `<div class="yb-chat-msg-avatar yb-chat-user-avatar"><span>👤</span></div><div class="yb-chat-msg-bubble">${text}</div>`
body.appendChild(msg)
body.scrollTop = body.scrollHeight
},
handleAction(action, param, text, url) {
this.addUserMsg(text || param)
if (action === 'retrySubmit') {
setTimeout(() => this.submitForm(), 200)
return
}
if (action === 'browse') {
setTimeout(() => {
this.addBotMsg('好的~为您推荐我们的热门产品 👇', [
{ text: 'E投标', desc: '更智能的标书写作引擎', action: 'goExternal', url: 'https://bid-ocai-v2.jinan.opencomputing.cn/', type: 'cards' },
{ text: '合同智能审查', desc: 'AI驱动合同风险审查效率提升60%+', action: 'goContract', type: 'cards' },
{ text: '投策智能体', desc: '分钟级投研分析,精准辅助投资决策', action: 'goInvest', type: 'cards' }
])
setTimeout(() => {
this.addBotMsg('或者您还有其他想了解的产品吗?', this.getProductTags(), 'tags')
}, 1200)
}, 200)
return
}
if (action === 'direct') {
setTimeout(() => {
this.addBotMsg('您想了解哪方面的产品或服务呢?', this.getProductTags(), 'tags')
}, 200)
return
}
if (action === 'goContract') {
vm.$router.push(this._config.pages['合同智能审查'] || '/homePage/agentStore/contractCase').catch(() => {})
this.close()
setTimeout(() => {
this.addBotMsg('已为您打开合同智能审查页面 📄<br>还有其他想了解的吗?', this.getProductTags(), 'tags')
}, 200)
return
}
if (action === 'goExternal') {
window.open(url, '_blank')
setTimeout(() => {
this.addBotMsg('已为您打开相关页面。还有其他想了解的吗?', this.getProductTags(), 'tags')
}, 200)
return
}
if (action === 'goInvest') {
vm.$router.push(this._config.pages['投策智能体'] || '/homePage/agentStore/decisionCase').catch(() => {})
this.close()
setTimeout(() => {
this.addBotMsg('已为您打开投策智能体页面 📊<br>还有其他想了解的吗?', this.getProductTags(), 'tags')
}, 200)
return
}
if (action === 'productIntro') {
this._selectedProduct = param
const intro = this.getProductIntro(param)
setTimeout(() => {
this.addBotMsg(intro)
setTimeout(() => this.addContactForm(), 800)
}, 200)
return
}
},
getProductTags() {
return this._config.products.map(item => ({
text: item.key,
action: 'productIntro',
param: item.key
}))
},
getProductIntro(key) {
const product = this._config.products.find(item => item.key === key)
return product ? product.intro : '这是一款优秀的AI产品欢迎进一步了解'
},
addContactForm() {
const body = document.getElementById('yunbaoChatBody')
if (!body) return
const form = document.createElement('div')
form.className = 'yb-chat-msg'
form.innerHTML = `
<div class="yb-chat-msg-avatar"><img src="${this._config.avatarUrl}" alt="云宝"></div>
<div>
<div class="yb-chat-msg-bubble">
请留下您的联系方式我们的顾问将尽快与您联系 😊
<div class="yb-chat-form">
<input type="text" class="yb-chat-form-input" id="yunbaoChatName" placeholder="您的姓名">
<input type="tel" class="yb-chat-form-input" id="yunbaoChatPhone" placeholder="联系电话">
<input type="text" class="yb-chat-form-input" id="yunbaoChatCompany" placeholder="公司名称">
<button class="yb-chat-form-submit" id="yunbaoChatSubmit">提交咨询</button>
<div class="yb-chat-form-tip">信息仅用于顾问联系</div>
</div>
</div>
</div>
`
body.appendChild(form)
body.scrollTop = body.scrollHeight
document.getElementById('yunbaoChatSubmit').addEventListener('click', () => this.submitForm())
},
async submitForm() {
const name = document.getElementById('yunbaoChatName').value.trim()
const phone = document.getElementById('yunbaoChatPhone').value.trim()
const company = document.getElementById('yunbaoChatCompany').value.trim()
const submitBtn = document.getElementById('yunbaoChatSubmit')
if (!name || !phone) {
this.addBotMsg('请先填写姓名和联系电话哦~')
return
}
if (!/^1[3-9]\d{9}$/.test(phone)) {
this.addBotMsg('请输入正确的 11 位手机号哦~')
return
}
if (submitBtn && submitBtn.disabled) return
if (submitBtn) {
submitBtn.disabled = true
submitBtn.innerText = '提交中...'
}
this.addUserMsg('已提交联系方式')
const data = {
custom_type: '1',
name,
phone,
company,
email: '',
content: this._selectedProduct ? `我想咨询关于【${this._selectedProduct}】的产品信息` : '云宝对话咨询',
source: '官网',
url_link: window.location.href
}
try {
const response = await reqNewHomeConsult(data)
if (response && response.status) {
this.addBotMsg('收到!我们的顾问将尽快与您联系 🎉<br>感谢您的信任!')
return
}
this.addBotMsg((response && response.msg) || '提交失败了,您可以稍后重试。', [
{ text: '重新提交', action: 'retrySubmit', primary: true }
])
} catch (error) {
this.addBotMsg('网络开小差了,提交失败,您可以重试一次。', [
{ text: '重新提交', action: 'retrySubmit', primary: true }
])
} finally {
if (submitBtn) {
submitBtn.disabled = false
submitBtn.innerText = '提交咨询'
}
}
}
}
},
getYunbaoChatHTML(config) {
return `
<div id="yunbaoModal" class="yb-assistant-modal-overlay">
<div class="yb-assistant-modal">
<div class="yb-assistant-modal-header">
<div class="yb-chat-avatar"><img src="${config.avatarUrl}" alt="云宝"></div>
<div class="yb-chat-header-info">
<div class="yb-chat-header-name">${config.name}</div>
<div class="yb-chat-header-status">在线</div>
</div>
<button class="yb-assistant-close-btn" id="yunbaoCloseBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
</div>
<div class="yb-chat-body" id="yunbaoChatBody"></div>
</div>
</div>
`
},
getYunbaoChatCSS() {
return `
.yb-assistant-modal-overlay{position:fixed;bottom:100px;right:24px;z-index:9999;display:none}
.yb-assistant-modal-overlay.active{display:block}
.yb-assistant-modal{background:rgba(255,255,255,0.85);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-radius:20px;width:400px;height:560px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 8px 32px rgba(59,130,246,0.08),0 0 0 1px rgba(255,255,255,0.6) inset;border:1px solid rgba(255,255,255,0.5);animation:ybChatSlideIn 0.35s cubic-bezier(0.34,1.56,0.64,1)}
@keyframes ybChatSlideIn{from{opacity:0;transform:translateY(20px) scale(0.95)}to{opacity:1;transform:translateY(0) scale(1)}}
@keyframes ybToastIn{from{opacity:0;transform:translateX(-50%) translateY(-10px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}
@keyframes ybToastOut{from{opacity:1;transform:translateX(-50%) translateY(0)}to{opacity:0;transform:translateX(-50%) translateY(-10px)}}
.yb-assistant-modal-header{background:linear-gradient(135deg,rgba(240,247,255,0.9) 0%,rgba(255,255,255,0.7) 100%);backdrop-filter:blur(10px);padding:16px 20px;display:flex;align-items:center;gap:12px;flex-shrink:0;border-bottom:1px solid rgba(59,130,246,0.08)}
.yb-chat-avatar{width:48px;height:48px;border-radius:50%;overflow:visible;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.yb-chat-avatar img{width:48px;height:48px;object-fit:contain;display:block}
.yb-chat-header-info{flex:1}
.yb-chat-header-name{font-size:15px;font-weight:600;color:#1e293b}
.yb-chat-header-status{font-size:12px;color:#64748b;display:flex;align-items:center;gap:4px}
.yb-chat-header-status:before{content:"";width:6px;height:6px;border-radius:50%;background:#22c55e;box-shadow:0 0 6px rgba(34,197,94,0.4)}
.yb-assistant-close-btn{width:32px;height:32px;background:rgba(0,0,0,0.04);border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all 0.2s;border:none}
.yb-assistant-close-btn:hover{background:rgba(0,0,0,0.08)}
.yb-assistant-close-btn svg{width:18px;height:18px;stroke:#64748b}
.yb-chat-body{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:16px;background:linear-gradient(180deg,rgba(240,247,255,0.3) 0%,rgba(255,255,255,0.1) 100%)}
.yb-chat-body::-webkit-scrollbar{width:4px}
.yb-chat-body::-webkit-scrollbar-thumb{background:rgba(59,130,246,0.15);border-radius:4px}
.yb-chat-msg{display:flex;gap:10px;animation:ybChatMsgIn 0.3s ease}
@keyframes ybChatMsgIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.yb-chat-msg-avatar{width:40px;height:40px;border-radius:50%;overflow:visible;flex-shrink:0;margin-top:2px;display:flex;align-items:center;justify-content:center}
.yb-chat-msg-avatar img{width:40px;height:40px;object-fit:contain;display:block}
.yb-chat-user-avatar{background:#e2e8f0;font-size:16px;overflow:hidden}
.yb-chat-user-avatar span{display:flex;align-items:center;justify-content:center;width:100%;height:100%}
.yb-chat-msg-bubble{background:#fff;border-radius:16px 16px 16px 4px;padding:12px 16px;font-size:14px;line-height:1.6;color:#334155;max-width:300px;box-shadow:0 1px 3px rgba(0,0,0,0.06)}
.yb-chat-msg-user{flex-direction:row-reverse}
.yb-chat-msg-user .yb-chat-msg-bubble{background:#1e293b;color:#fff;border-radius:16px 16px 4px 16px}
.yb-chat-options{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
.yb-chat-option-btn{padding:8px 16px;border-radius:20px;border:1px solid rgba(59,130,246,0.2);background:rgba(255,255,255,0.8);backdrop-filter:blur(4px);font-size:13px;color:#3b82f6;cursor:pointer;transition:all 0.2s;white-space:nowrap}
.yb-chat-option-btn:hover{border-color:#3b82f6;background:rgba(59,130,246,0.06);box-shadow:0 2px 8px rgba(59,130,246,0.1)}
.yb-chat-option-btn.primary{background:linear-gradient(135deg,#3b82f6,#2563eb);color:#fff;border-color:transparent;box-shadow:0 2px 8px rgba(59,130,246,0.25)}
.yb-chat-product-cards{display:flex;flex-direction:column;gap:10px;margin-top:10px}
.yb-chat-product-card{background:rgba(255,255,255,0.7);border:1px solid rgba(59,130,246,0.1);border-radius:12px;padding:10px 14px!important;height:92px!important;min-height:92px!important;max-height:92px!important;cursor:pointer;transition:all 0.2s;backdrop-filter:blur(4px);box-sizing:border-box;overflow:hidden;display:flex!important;flex-direction:column;justify-content:flex-start}
.yb-chat-product-card:hover{border-color:rgba(59,130,246,0.25);background:rgba(255,255,255,0.9);box-shadow:0 4px 16px rgba(59,130,246,0.08);transform:translateY(-1px)}
.yb-chat-product-card-title{font-size:14px;font-weight:600;color:#1e293b;margin-bottom:4px;line-height:1.3}
.yb-chat-product-card-desc{font-size:12px;color:#64748b;line-height:1.35;margin-bottom:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.yb-chat-product-card-link{font-size:12px;color:#3b82f6;font-weight:500;display:inline-flex;align-items:center;gap:4px;line-height:1.2}
.yb-chat-tag-options{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.yb-chat-tag-btn{padding:6px 12px;border-radius:16px;border:1px solid rgba(59,130,246,0.15);background:rgba(255,255,255,0.7);font-size:12px;color:#3b82f6;cursor:pointer;transition:all 0.2s;backdrop-filter:blur(4px)}
.yb-chat-tag-btn:hover{border-color:#3b82f6;background:rgba(59,130,246,0.06);box-shadow:0 2px 8px rgba(59,130,246,0.1)}
.yb-chat-form{margin-top:10px;display:flex;flex-direction:column;gap:10px}
.yb-chat-form-input{width:100%;padding:10px 14px;border:1px solid rgba(59,130,246,0.15);border-radius:10px;font-size:13px;color:#1e293b;background:rgba(255,255,255,0.8);outline:none;transition:all 0.2s;box-sizing:border-box}
.yb-chat-form-input:focus{border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59,130,246,0.08);background:#fff}
.yb-chat-form-input::placeholder{color:#94a3b8}
.yb-chat-form-submit{width:100%;padding:10px;background:#1a1a2e;color:#fff;border:none;border-radius:9999px;font-size:14px;font-weight:500;cursor:pointer;transition:all 0.2s;box-shadow:0 2px 8px rgba(26,26,46,0.2)}
.yb-chat-form-submit:hover{background:#0f0f1a;box-shadow:0 4px 12px rgba(26,26,46,0.3);transform:translateY(-1px)}
.yb-chat-form-submit:disabled{background:#cbd5e1;box-shadow:none;cursor:not-allowed}
.yb-chat-form-tip{font-size:11px;color:#94a3b8;text-align:center}
@media(max-width:480px){.yb-assistant-modal{width:calc(100vw - 32px);height:calc(100vh - 140px);right:0}.yb-assistant-modal-overlay{right:16px;bottom:90px}}
`
},
//
closeWindow() {
@ -345,6 +724,7 @@ export default {
</script>
<style lang="scss" scoped>
.box {
.contener {
.dialog {
width: 400px;

View File

@ -179,6 +179,7 @@ export const constantRoutes = [
title: "H5关于我们", fullPath: "/h5about/index",
},
},
{
path: '/beforeLogin',
name: 'BeforeLogin',

View File

@ -429,6 +429,13 @@ export default {
margin-bottom: 18px;
}
::v-deep .el-form-item__content,
::v-deep .el-input,
::v-deep .el-select,
::v-deep .el-textarea {
width: 100%;
}
::v-deep .el-form-item__label {
padding-bottom: 6px;
font-size: 13px;
@ -465,6 +472,7 @@ export default {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
width: 100%;
}
.option-item {
@ -473,6 +481,10 @@ export default {
border: 1px solid #e5e7eb;
border-radius: 10px;
white-space: nowrap;
box-sizing: border-box;
display: flex;
align-items: center;
width: 100%;
::v-deep .el-radio__label {
padding-left: 6px;
@ -489,6 +501,7 @@ export default {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.direction-item {
@ -498,6 +511,8 @@ export default {
padding: 10px;
display: flex;
align-items: flex-start;
width: 100%;
box-sizing: border-box;
::v-deep .el-checkbox__label {
display: inline-flex;

View File

@ -3,7 +3,7 @@
<el-dialog
title="联系销售"
:visible="dialogVisible"
width="580px"
width="400px"
center
append-to-body
:close-on-click-modal="false"
@ -15,34 +15,64 @@
<div class="talk-body">
<div class="talk-form">
<el-form ref="ruleForm" :rules="rules" label-position="top" label-width="80px" :model="addData">
<el-form-item label="需求描述">
<el-input :autosize="{ minRows: 4, maxRows: 4 }" type="textarea" v-model="addData.content" placeholder="请输入您的具体需求"></el-input>
</el-form-item>
<el-form-item label="客户类型">
<el-radio v-model="addData.custom_type" label="1">企业</el-radio>
<el-radio v-model="addData.custom_type" label="0">个人</el-radio>
</el-form-item>
<div class="form-grid">
<el-form-item label="联系人姓名" prop="name">
<el-input v-model="addData.name" placeholder="请输入联系人姓名"></el-input>
<el-form-item label="1. 请填写您的姓名" prop="name">
<el-input v-model.trim="addData.name" maxlength="20" placeholder="请输入您的姓名"></el-input>
</el-form-item>
<el-form-item prop="phone" label="联系人手机">
<el-input v-model="addData.phone" placeholder="请输入联系人手机"></el-input>
<el-form-item prop="phone" label="2. 请填写您的联系方式">
<el-input v-model.trim="addData.phone" maxlength="11" placeholder="请输入您的联系电话"></el-input>
</el-form-item>
<el-form-item v-show="addData.custom_type==='1'" label="公司名称" class="form-item--compact">
<el-input v-model="addData.company" placeholder="请输入公司名称"></el-input>
<el-form-item label="3. 请填写您的邮箱" prop="email" class="form-item--compact">
<el-input v-model.trim="addData.email" maxlength="60" placeholder="请输入您的邮箱"></el-input>
</el-form-item>
<el-form-item label="联系人邮箱" class="form-item--compact">
<el-input v-model="addData.email" placeholder="请输入联系人邮箱"></el-input>
<el-form-item label="4. 请填写您的公司名称" prop="company" class="form-item--compact">
<el-input v-model.trim="addData.company" maxlength="80" placeholder="请输入您的公司名称"></el-input>
</el-form-item>
<el-form-item label="5. 请选择您的企业类型(单选)" prop="enterprise_type" class="form-item--full">
<el-radio-group v-model="addData.enterprise_type" class="option-grid">
<el-radio
v-for="item in enterpriseOptions"
:key="item.id"
:label="item.id"
class="option-item"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="6. 请选择您所在的省份(单选)" prop="region" class="form-item--full">
<el-select
v-model="addData.region"
filterable
placeholder="输入省份名称或拼音首字母搜索..."
class="full-select"
>
<el-option
v-for="item in provinceOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="7. 如果您有其他问题需要咨询,请留言" class="form-item--message">
<el-input
v-model.trim="addData.content"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
maxlength="500"
show-word-limit
placeholder="请输入您想咨询的内容..."
></el-input>
</el-form-item>
</div>
</el-form>
<el-checkbox class="agreement-checkbox" v-model="checked">
勾选表示您同意<span v-if="JSON.stringify(logoInfoNew)!=='{}'">{{ logoInfoNew.home.bannerTitle }}</span>及其授权的合作伙伴通过您填写的联系方式联系您
您填写的信息仅用于本次业务对接沟通公司将严格落实信息安全保护机制不泄露不滥用您的任何个人资料
</el-checkbox>
</div>
<div class="qcode">
<img v-if="JSON.stringify(logoInfoNew)!=='{}'" :src="logoInfoNew.home.qrCode" alt="">
<div v-if="qrCodeUrl" class="qcode">
<img :src="qrCodeUrl" alt="官方客服二维码">
<span>扫码添加官方客服</span>
</div>
</div>
@ -57,28 +87,48 @@
import Vue from 'vue'
import {mapState} from "vuex";
import {reqNewHomeConsult} from "@/api/newHome";
import {reqConsultForm} from "@/api/H5";
export default Vue.extend({
name: "talk",
data() {
const validatePhone = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入联系电话'))
} else if (!/^1[3-9]\d{9}$/.test(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
}
return {
rules: {
name: [
{required: true, message: '请输入姓名', trigger: 'blur'},
],
phone: [
{required: true, message: '请输入手机号', trigger: 'change'},
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
{required: true, validator: validatePhone, trigger: 'blur'}
],
email: [
{required: true, message: '请输入邮箱', trigger: 'blur'},
{type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur'}
],
company: [
{required: true, message: '请输入公司名称', trigger: 'blur'}
],
enterprise_type: [
{required: true, message: '请选择企业类型', trigger: 'change'}
],
region: [
{required: true, message: '请选择所在省份', trigger: 'change'}
]
},
addBtnLoading: false,
checked: false,
dialogVisible: false,
enterpriseOptions: [],
provinceOptions: [],
addData: {
content: '',//
custom_type: "1",// 0- 1-
@ -86,6 +136,8 @@ export default Vue.extend({
phone: "",//
company: "",//
email: "",//
enterprise_type: '',
region: '',
},
labelPosition: 'right',
formLabelAlign: {
@ -100,10 +152,54 @@ export default Vue.extend({
immediate: true,
handler(value) {
this.dialogVisible = value
if (value) {
this.fetchConsultOptions()
}
}
}
},
methods: {
getDefaultFormData() {
return {
content: '',
custom_type: '1',
name: '',
phone: '',
company: '',
email: '',
enterprise_type: '',
region: '',
}
},
normalizeDictOptions(list, type) {
return list
.filter(item => item && item.dict_type === type)
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0))
.map(item => ({
id: item.dict_key,
name: item.dict_value
}))
},
extractConsultOptionList(res) {
if (res && Array.isArray(res.data)) return res.data
if (res && res.data && Array.isArray(res.data.data)) return res.data.data
return []
},
async fetchConsultOptions() {
try {
const res = await reqConsultForm()
const list = this.extractConsultOptionList(res)
if (!list.length) return
const enterpriseOptions = this.normalizeDictOptions(list, 'enterprise_type')
const provinceOptions = this.normalizeDictOptions(list, 'region')
if (enterpriseOptions.length) this.enterpriseOptions = enterpriseOptions
if (provinceOptions.length) this.provinceOptions = provinceOptions
} catch (error) {
//
}
},
handleDialogVisibleUpdate(value) {
this.dialogVisible = value;
if (!value) {
@ -113,7 +209,6 @@ export default Vue.extend({
cancelBtn() {
this.dialogVisible = false;
this.$store.commit('setShowTalk', false);
console.log("取消按钮被点击了");
},
confirmBtn() {
if (!this.checked) {
@ -123,8 +218,12 @@ export default Vue.extend({
this.$refs['ruleForm'].validate((valid) => {
if (valid) {
this.addBtnLoading = true
this.addData.url_link = window.location.href
reqNewHomeConsult(this.addData).then(response => {
const submitData = {
...this.addData,
source: '官网',
url_link: window.location.href
}
reqNewHomeConsult(submitData).then(response => {
this.addBtnLoading = false
if (response.status) {
this.$message({
@ -132,14 +231,8 @@ export default Vue.extend({
message: '感谢您关注人工智能服务平台,我们将尽快联系您!~'
});
this.$store.commit('setShowTalk', false);
this.addData = {
content: '',
custom_type: "1",
name: "",
phone: "",
company: "",
email: "",
}
this.addData = this.getDefaultFormData()
this.checked = false
} else {
this.$message.error(response.msg || '提交失败,请稍后再试!');
}
@ -161,6 +254,11 @@ export default Vue.extend({
showTalk: (state) => state.product.showTalk,
logoInfoNew: state => state.product.logoInfoNew,
}),
qrCodeUrl() {
return this.logoInfoNew && this.logoInfoNew.home && this.logoInfoNew.home.qrCode
? this.logoInfoNew.home.qrCode
: ''
},
},
})
</script>
@ -171,7 +269,8 @@ export default Vue.extend({
border-radius: 22px;
overflow: hidden;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.18);
z-index: 99999999999999999!important;
// z-index: 99999999999999999!important;
width: 600px !important;
}
@ -197,7 +296,7 @@ export default Vue.extend({
}
::v-deep .el-dialog__body {
padding: 6px 22px 4px;
padding: 6px 24px 4px;
}
::v-deep .el-form-item__label {
@ -209,7 +308,14 @@ export default Vue.extend({
}
::v-deep .el-form-item {
margin-bottom: 14px;
margin-bottom: 18px;
}
::v-deep .el-form-item__error {
position: static;
padding-top: 6px;
line-height: 1.25;
font-size: 12px;
}
::v-deep .el-input__inner,
@ -241,26 +347,70 @@ export default Vue.extend({
position: relative;
min-height: 430px;
padding-bottom: 8px;
display: block;
}
.talk-form {
width: 100%;
min-width: 0;
padding-right: 0;
}
::v-deep .talk-form .el-form-item__content,
::v-deep .talk-form .el-input,
::v-deep .talk-form .el-select,
::v-deep .talk-form .el-textarea {
width: 100%;
}
.form-grid {
display: block;
}
.form-item--compact {
width: calc(100% - 164px);
width: 100%;
}
.form-item--full {
width: 100%;
}
.form-item--message {
width: calc(100% - 184px);
}
.full-select {
width: 100%;
}
.option-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
width: 100%;
}
.option-item {
margin-right: 0 !important;
padding: 9px 8px;
border: 1px solid #e5eaf3;
border-radius: 12px;
white-space: nowrap;
box-sizing: border-box;
width: 100%;
::v-deep .el-radio__label {
padding-left: 6px;
color: #374151;
font-size: 13px;
}
}
.agreement-checkbox {
margin-top: 8px;
max-width: calc(100% - 164px);
max-width: calc(100% - 184px);
display: inline-flex;
align-items: center;
align-items: flex-start;
::v-deep .el-checkbox__label {
color: #6b7280;
@ -270,17 +420,17 @@ export default Vue.extend({
}
::v-deep .el-checkbox__input {
align-self: center;
margin-top: 4px;
}
}
.qcode {
width: 140px;
width: 150px;
min-height: 162px;
padding: 12px 10px;
position: absolute;
right: 0;
bottom: 8px;
bottom: 18px;
border-radius: 18px;
background: linear-gradient(180deg, #f4f8ff 0%, #ffffff 100%);
border: 1px solid #edf1f7;
@ -336,8 +486,15 @@ export default Vue.extend({
}
@media (max-width: 900px) {
::v-deep .talk-dialog {
width: 92vw !important;
}
.talk-body {
min-height: auto;
display: block;
padding-right: 0;
padding-bottom: 8px;
}
.talk-form {
@ -354,5 +511,17 @@ export default Vue.extend({
.form-item--compact {
width: 100%;
}
.form-item--full {
width: 100%;
}
.form-item--message {
width: 100%;
}
.agreement-checkbox {
max-width: 100%;
}
}
</style>

View File

@ -55,15 +55,26 @@
</div>
<div class="action-bar">
<el-button
type="success"
icon="el-icon-download"
size="small"
:loading="exportLoading"
@click="getTableData('1')"
>
导出Excel
</el-button>
<div class="batch-actions">
<el-button
type="success"
icon="el-icon-download"
size="small"
:loading="exportLoading"
@click="getTableData('1')"
>
导出Excel
</el-button>
<el-button type="primary" size="small" :loading="actionLoading" :disabled="!selectedRows.length" @click="batchUpdateFeedback('1')">
批量标记沟通中
</el-button>
<el-button type="warning" size="small" :loading="actionLoading" :disabled="!selectedRows.length" @click="batchUpdateFeedback('0')">
批量标记待回复
</el-button>
<el-button type="danger" size="small" :loading="actionLoading" :disabled="!selectedRows.length" @click="batchDelete">
批量删除
</el-button>
</div>
<div class="record-count"> {{ totalCount }} 条记录</div>
</div>
@ -74,7 +85,9 @@
class="consulting-table"
empty-text="暂无咨询数据"
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="46" align="center" />
<el-table-column label="ID" width="80" align="center">
<template slot-scope="scope">
{{ getRowNo(scope.$index) }}
@ -83,21 +96,42 @@
<el-table-column prop="name" label="姓名" min-width="90" />
<el-table-column prop="phone" label="手机号" min-width="120" />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip />
<el-table-column prop="company" label="公司名称" min-width="140" show-overflow-tooltip />
<el-table-column prop="source" label="咨询渠道" min-width="120" show-overflow-tooltip />
<el-table-column label="公司名称/企业规模" min-width="180" show-overflow-tooltip>
<template slot-scope="scope">
<div class="company-cell">
<span class="company-name">{{ scope.row.company || '-' }}</span>
<span class="company-scale">企业规模{{ scope.row.enterprise_type_name || '-' }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="咨询渠道" min-width="120" align="center">
<template slot-scope="scope">
<span :class="['source-tag', getSourceClass(scope.row.source)]">
{{ scope.row.source || '-' }}
</span>
</template>
</el-table-column>
<el-table-column prop="consult_direction_name" label="咨询方向" min-width="220" show-overflow-tooltip />
<el-table-column prop="content" label="需求描述" min-width="220" show-overflow-tooltip />
<el-table-column prop="update_time" label="提交时间" min-width="160" />
<el-table-column label="回复状态" min-width="110" align="center">
<template slot-scope="scope">
<el-tag size="mini" effect="plain" :type="feedbackTagType(scope.row.feedback)">
<el-tag
size="mini"
effect="plain"
class="feedback-tag"
:type="feedbackTagType(scope.row.feedback)"
@click="toggleFeedback(scope.row)"
>
{{ feedbackText(scope.row) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<el-table-column label="操作" width="110" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small">查看</el-button>
<el-button type="text" size="small" @click="replyUser(scope.row)">回复</el-button>
<el-button type="text" size="small" @click="viewUser(scope.row)">详情</el-button>
<el-button type="text" size="small" text="warning" @click="openRemark(scope.row)">备注</el-button>
<!-- <el-button type="text" size="small" class="danger-text" @click="deleteRows([scope.row])">删除</el-button> -->
</template>
</el-table-column>
</el-table>
@ -115,11 +149,112 @@
/>
</div>
</div>
<el-dialog
title="咨询详情"
:visible.sync="detailVisible"
width="600px"
append-to-body
custom-class="detail-dialog"
top="8vh"
>
<div v-if="currentRow" class="detail-panel">
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">姓名</span>
<strong>{{ currentRow.name || '-' }}</strong>
</div>
<div class="detail-item">
<span class="detail-label">联系电话</span>
<strong>{{ currentRow.phone || '-' }}</strong>
</div>
<div class="detail-item">
<span class="detail-label">邮箱</span>
<strong>{{ currentRow.email || '-' }}</strong>
</div>
<div class="detail-item">
<span class="detail-label">公司名称</span>
<strong>{{ currentRow.company || '-' }}</strong>
</div>
<div class="detail-item">
<span class="detail-label">企业类型</span>
<strong>{{ currentRow.enterprise_type_name || currentRow.custom_type_name || '-' }}</strong>
</div>
<div class="detail-item">
<span class="detail-label">咨询渠道</span>
<strong>{{ currentRow.source || '-' }}</strong>
</div>
<div class="detail-item">
<span class="detail-label">提交时间</span>
<strong>{{ currentRow.update_time || currentRow.create_at || '-' }}</strong>
</div>
</div>
<div class="detail-section">
<span class="detail-label">咨询方向</span>
<strong>{{ currentRow.consult_direction_name || '-' }}</strong>
</div>
<div class="detail-section">
<span class="detail-label">需求描述/留言</span>
<div class="detail-content-box">{{ currentRow.content || '-' }}</div>
</div>
<div class="detail-divider"></div>
<div class="detail-section">
<span class="detail-label">回复状态</span>
<el-tag size="small" effect="plain" :type="feedbackTagType(currentRow.feedback)">
{{ feedbackText(currentRow) }}
</el-tag>
</div>
<div class="detail-section">
<span class="detail-label">备注信息</span>
<div class="detail-content-box detail-remark-text">{{ currentRow.remark || '暂无备注' }}</div>
<div class="detail-remark-row">
<el-input
v-model.trim="detailRemarkText"
maxlength="300"
placeholder="添加备注..."
@keyup.enter.native="saveDetailRemark"
/>
<el-button type="primary" class="detail-save-btn" :loading="actionLoading" @click="saveDetailRemark">
保存
</el-button>
</div>
</div>
</div>
</el-dialog>
<el-dialog
title="添加备注"
:visible.sync="remarkVisible"
width="520px"
append-to-body
custom-class="remark-dialog"
:close-on-click-modal="true"
@close="saveRemarkOnClose"
>
<div class="remark-content">
<label class="remark-label">备注内容</label>
<el-input
v-model.trim="remarkText"
type="textarea"
:rows="6"
maxlength="300"
show-word-limit
placeholder="请输入备注信息..."
/>
<p class="remark-tip">点击弹窗外任意位置或关闭弹窗将自动保存备注</p>
</div>
</el-dialog>
</div>
</template>
<script>
import { reqSearchUserInquiry } from '@/api/ncmatch'
import { reqSearchUserInquiry, reqUserInquiryDelete, reqUserInquiryStatusSwitch } from '@/api/ncmatch'
import XLSX from 'xlsx'
export default {
@ -129,9 +264,17 @@ export default {
tableData: [],
tableLoading: false,
exportLoading: false,
actionLoading: false,
page: 1,
pageSize: 10,
totalCount: 0,
selectedRows: [],
detailVisible: false,
remarkVisible: false,
remarkText: '',
detailRemarkText: '',
originalRemarkText: '',
currentRow: null,
sourceList: [],
queryForm: {
name: '',
@ -186,6 +329,18 @@ export default {
if (value === '3') return 'info'
return 'warning'
},
getSourceClass(source) {
const value = String(source || '')
if (value.includes('公众号')) return 'source-tag-green'
if (value.includes('官网')) return 'source-tag-blue'
return 'source-tag-gray'
},
getRowId(row) {
return row && (row.id || row.inquiry_id || row.user_inquiry_id)
},
buildIds(rows) {
return JSON.stringify(rows.map(row => this.getRowId(row)).filter(Boolean))
},
getRowNo(index) {
return (this.page - 1) * this.pageSize + index + 1
},
@ -229,8 +384,13 @@ export default {
extractSourceList(res, rows = []) {
const data = (res && res.data) || {}
const fromApi = data.source_list || data.sourceList || res.source_list || res.sourceList
if (Array.isArray(fromApi) && fromApi.length) return fromApi
return [...new Set(rows.map(item => String(item.source || '').trim()).filter(Boolean))]
const apiList = Array.isArray(fromApi) ? fromApi : []
const rowList = rows.map(item => item.source)
return [...new Set(
[...this.sourceList, ...apiList, ...rowList]
.map(item => String(item || '').trim())
.filter(Boolean)
)]
},
getTableData(to_excel) {
const params = this.buildSearchParams(to_excel)
@ -249,6 +409,7 @@ export default {
this.tableData = rows
this.totalCount = this.extractTotal(res, rows.length)
this.sourceList = this.extractSourceList(res, rows)
this.selectedRows = []
return
}
@ -288,18 +449,111 @@ export default {
this.page = page
this.getTableData('0')
},
async replyUser(row) {
if (row.email) {
window.location.href = `mailto:${row.email}`
handleSelectionChange(rows) {
this.selectedRows = rows
},
async updateInquiryStatus(rows, feedback, remark) {
const ids = this.buildIds(rows)
if (!ids || ids === '[]') {
this.$message.warning('请选择有效数据')
return false
}
const params = {
ids,
feedback: String(feedback)
}
if (remark !== undefined) params.remark = remark
this.actionLoading = true
try {
const res = await reqUserInquiryStatusSwitch(params)
if (res && res.status) {
this.$message.success('操作成功')
this.getTableData('0')
return true
}
this.$message.error((res && res.msg) || '操作失败')
return false
} catch (error) {
this.$message.error('操作失败,请稍后重试')
return false
} finally {
this.actionLoading = false
}
},
toggleFeedback(row) {
const nextFeedback = String(row.feedback) === '0' ? '1' : '0'
this.updateInquiryStatus([row], nextFeedback)
},
batchUpdateFeedback(feedback) {
this.updateInquiryStatus(this.selectedRows, feedback)
},
async deleteRows(rows) {
const ids = this.buildIds(rows)
if (!ids || ids === '[]') {
this.$message.warning('请选择有效数据')
return
}
if (row.phone) {
try {
await navigator.clipboard.writeText(row.phone)
this.$message.success('手机号已复制,可直接联系用户')
} catch (error) {
this.$message.warning('联系方式复制失败,请手动联系')
try {
await this.$confirm('确认删除选中的咨询记录吗?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
})
} catch (error) {
return
}
this.actionLoading = true
try {
const res = await reqUserInquiryDelete({ ids })
if (res && res.status) {
this.$message.success('删除成功')
this.getTableData('0')
return
}
this.$message.error((res && res.msg) || '删除失败')
} catch (error) {
this.$message.error('删除失败,请稍后重试')
} finally {
this.actionLoading = false
}
},
batchDelete() {
this.deleteRows(this.selectedRows)
},
viewUser(row) {
this.currentRow = row
this.detailRemarkText = row.remark || ''
this.detailVisible = true
},
openRemark(row) {
this.currentRow = row
this.remarkText = row.remark || ''
this.originalRemarkText = this.remarkText
this.remarkVisible = true
},
saveRemarkOnClose() {
if (!this.currentRow) return
if (this.remarkText === this.originalRemarkText) return
this.updateInquiryStatus(
[this.currentRow],
this.currentRow.feedback === undefined || this.currentRow.feedback === null ? '0' : this.currentRow.feedback,
this.remarkText
)
},
async saveDetailRemark() {
if (!this.currentRow) return
const success = await this.updateInquiryStatus(
[this.currentRow],
this.currentRow.feedback === undefined || this.currentRow.feedback === null ? '0' : this.currentRow.feedback,
this.detailRemarkText
)
if (success) {
this.currentRow.remark = this.detailRemarkText
}
}
}
@ -414,6 +668,17 @@ export default {
margin-bottom: 16px;
}
.batch-actions {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.batch-actions ::v-deep .el-button + .el-button {
margin-left: 0;
}
.record-count {
color: #6b7280;
font-size: 13px;
@ -442,6 +707,240 @@ export default {
background: #f9fafb;
}
.company-cell {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
}
.company-name {
color: #1f2937;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.company-scale {
color: #8a94a6;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.source-tag {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 52px;
height: 24px;
padding: 0 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
line-height: 24px;
}
.source-tag-green {
color: #15803d;
background: #dcfce7;
}
.source-tag-blue {
color: #1d4ed8;
background: #dbeafe;
}
.source-tag-gray {
color: #64748b;
background: #f1f5f9;
}
.feedback-tag {
cursor: pointer;
user-select: none;
}
.danger-text {
color: #f56c6c;
}
::v-deep .detail-dialog {
border-radius: 24px;
overflow: hidden;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.22);
}
::v-deep .detail-dialog .el-dialog__header {
padding: 28px 34px 22px;
border-bottom: 1px solid #eef2f7;
}
::v-deep .detail-dialog .el-dialog__title {
color: #111827;
font-size: 24px;
font-weight: 700;
}
::v-deep .detail-dialog .el-dialog__headerbtn {
top: 30px;
right: 34px;
font-size: 22px;
}
::v-deep .detail-dialog .el-dialog__body {
padding: 28px 34px 30px;
}
.detail-panel {
color: #111827;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 28px 72px;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 6px;
word-break: break-word;
}
.detail-label {
display: block;
margin-bottom: 8px;
color: #8a94a6;
font-size: 14px;
line-height: 1.4;
}
.detail-item strong,
.detail-section strong {
color: #111827;
font-size: 16px;
line-height: 1.5;
font-weight: 700;
}
.detail-section {
margin-top: 28px;
}
.detail-content-box {
min-height: 42px;
padding: 14px 16px;
border-radius: 8px;
background: #f8fafc;
color: #1f2937;
font-size: 15px;
line-height: 1.7;
word-break: break-word;
}
.detail-remark-text {
color: #d97706;
}
.detail-divider {
height: 1px;
margin: 24px 0;
background: #edf1f7;
}
.detail-remark-row {
margin-top: 14px;
display: flex;
align-items: center;
gap: 12px;
}
.detail-remark-row ::v-deep .el-input__inner {
height: 42px;
border-radius: 8px;
}
.detail-save-btn {
width: 72px;
height: 42px;
border: 0;
border-radius: 8px;
background: #9333ea;
font-weight: 600;
}
::v-deep .remark-dialog {
border-radius: 24px;
overflow: hidden;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.22);
}
::v-deep .remark-dialog .el-dialog__header {
padding: 28px 34px 22px;
border-bottom: 1px solid #eef2f7;
}
::v-deep .remark-dialog .el-dialog__title {
color: #111827;
font-size: 24px;
font-weight: 700;
}
::v-deep .remark-dialog .el-dialog__headerbtn {
top: 30px;
right: 34px;
font-size: 22px;
}
::v-deep .remark-dialog .el-dialog__body {
padding: 28px 34px 30px;
}
.remark-content {
display: flex;
flex-direction: column;
}
.remark-label {
margin-bottom: 12px;
color: #1f2937;
font-size: 16px;
font-weight: 600;
}
.remark-content ::v-deep .el-textarea__inner {
min-height: 150px !important;
padding: 16px 18px;
border: 1px solid #dfe6f0;
border-radius: 12px;
color: #111827;
font-size: 15px;
line-height: 1.7;
resize: vertical;
}
.remark-content ::v-deep .el-textarea__inner::placeholder {
color: #a0aec0;
}
.remark-content ::v-deep .el-input__count {
right: 14px;
bottom: 10px;
color: #9ca3af;
background: transparent;
}
.remark-tip {
margin: 12px 0 0;
color: #8a94a6;
font-size: 13px;
line-height: 1.6;
}
.pager-wrap {
display: flex;
align-items: center;