Commit daff13a5 by yuzhenWang

客户资料模块发布测试

parent 04757222
...@@ -4,5 +4,7 @@ VITE_APP_TITLE = CSF-CORE ...@@ -4,5 +4,7 @@ VITE_APP_TITLE = CSF-CORE
# 开发环境配置 # 开发环境配置
VITE_APP_ENV = 'development' VITE_APP_ENV = 'development'
# 若依管理系统/开发环境 # 测试前缀 接口前缀 暂时是这个
VITE_APP_BASE_API = 'http://10.0.10.26:9002' VITE_APP_BASE_API = 'http://139.224.145.34:9002'
# 测试环境地址前缀 暂时是这个
VITE_APP_BASE_API1 = 'http://139.224.145.34:6699'
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
"@vueuse/core": "13.3.0", "@vueuse/core": "13.3.0",
"axios": "1.9.0", "axios": "1.9.0",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"dayjs": "^1.11.18",
"echarts": "5.6.0", "echarts": "5.6.0",
"element-plus": "2.9.9", "element-plus": "2.9.9",
"file-saver": "2.0.5", "file-saver": "2.0.5",
......
import request from '@/utils/request'
// 修改上传图片方法,添加正确的请求配置
export function uploadImage(data) {
console.log('data', data)
return request({
url: '/oss/api/oss/upload',
data: data,
method: 'post',
// 添加以下配置
headers: {
'Content-Type': 'multipart/form-data' // 明确指定为multipart类型
},
// 如果使用axios,需要设置这个参数
transformRequest: [
function (data) {
return data
}
]
})
}
// 获取系统左侧菜单
export function getMenuLists(projectBizId, tenantBizId) {
return request({
url: `/user/api/sysUser/login/permission/project/getMenuRouters?projectBizId=${projectBizId}&tenantBizId=${tenantBizId}`,
method: 'get'
})
}
// 用token获取用户详细信息
export function getUserInfo(token) {
return request({
url: `/user/api/sysUser/getUserInfoByToken?token=${token}`,
method: 'get'
})
}
// 获取国家地区信息
export function getCountryInfo() {
return request({
url: `/user/api/mdCountry/group`,
method: 'get'
})
}
// 搜索国家地区信息
export function getSearchCountry(name) {
return request({
url: `/user/api/mdCountry/search?name=${name}`,
method: 'get'
})
}
import request from '@/utils/request'
// 查询fna列表
export function getFnaList(data) {
return request({
url: '/csf/api/Fna/list/page',
method: 'post',
data: data
})
}
/*
流程接口开始
*/
// 删除流程
export function deleteFna(data) {
return request({
url: '/csf/api/Fna/delete',
method: 'post',
data: data
})
}
// 新建流程
export function addFna(data) {
return request({
url: '/csf/api/Fna/add',
method: 'post',
data: data
})
}
// 查询流程详情
export function getProcessDetail(fnaBizId) {
return request({
url: `/csf/api/Fna/get/vo?fnaBizId=${fnaBizId}`,
method: 'get'
})
}
// 更新流程
export function updateProcess(data) {
return request({
url: '/csf/api/Fna/update',
method: 'post',
data: data
})
}
/*
流程接口结束
*/
/*
客户模块接口开始
*/
// 新增客户信息
export function addCustomer(data) {
return request({
url: '/csf/api/Customer/add',
method: 'post',
data: data
})
}
// 获取客户详情
export function getCustomerDetail(customerBizId) {
return request({
url: `/csf/api/Customer/get/vo?customerBizId=${customerBizId}`,
method: 'get'
})
}
// 修改客户信息
export function editCustomer(data) {
return request({
url: '/csf/api/Customer/update',
method: 'post',
data: data
})
}
// 修改客户信息
export function getCustomerList(data) {
return request({
url: '/csf/api/Customer/list/page/vo',
method: 'post',
data: data
})
}
/*
客户模块接口结束
*/
// 修改角色
export function roleUpdate(data) {
return request({
url: '/user/api/sysRole/edit',
method: 'put',
data: data
})
}
// 删除角色
export function delRole(roleId) {
return request({
url: '/system/role/' + roleId,
method: 'delete'
})
}
// 编辑角色状态
export function roleStatusChange(roleBizId, status) {
return request({
url: `/user/api/sysRole/edit/status?roleBizId=${roleBizId}&status=${status}`,
method: 'patch'
})
}
// 查询角色详情
export function getRoleDetail(roleBizId) {
return request({
url: `/user/api/sysRole/detail?roleBizId=${roleBizId}`,
method: 'get'
})
}
//作用域切换搜索
export function searchScopeList(data) {
return request({
url: '/user/api/sysUser/scope/page',
method: 'post',
data: data
})
}
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>iconfont Demo</title>
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
<link rel="stylesheet" href="demo.css">
<link rel="stylesheet" href="iconfont.css">
<script src="iconfont.js"></script>
<!-- jQuery -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
<!-- 代码高亮 -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
<style>
.main .logo {
margin-top: 0;
height: auto;
}
.main .logo a {
display: flex;
align-items: center;
}
.main .logo .sub-title {
margin-left: 0.5em;
font-size: 22px;
color: #fff;
background: linear-gradient(-45deg, #3967FF, #B500FE);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>
</head>
<body>
<div class="main">
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
</a></h1>
<div class="nav-tabs">
<ul id="tabs" class="dib-box">
<li class="dib active"><span>Unicode</span></li>
<li class="dib"><span>Font class</span></li>
<li class="dib"><span>Symbol</span></li>
</ul>
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=5015061" target="_blank" class="nav-more">查看项目</a>
</div>
<div class="tab-container">
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe114;</span>
<div class="name">已完成</div>
<div class="code-name">&amp;#xe114;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe113;</span>
<div class="name">延期未完成</div>
<div class="code-name">&amp;#xe113;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe112;</span>
<div class="name">导入</div>
<div class="code-name">&amp;#xe112;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe001;</span>
<div class="name">续期</div>
<div class="code-name">&amp;#xe001;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe002;</span>
<div class="name">跟进记录</div>
<div class="code-name">&amp;#xe002;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe003;</span>
<div class="name">预约</div>
<div class="code-name">&amp;#xe003;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe004;</span>
<div class="name">画笔</div>
<div class="code-name">&amp;#xe004;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe005;</span>
<div class="name">Home, homepage, menu</div>
<div class="code-name">&amp;#xe005;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe006;</span>
<div class="name">待结算</div>
<div class="code-name">&amp;#xe006;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe007;</span>
<div class="name">检查辅导</div>
<div class="code-name">&amp;#xe007;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe008;</span>
<div class="name">有效保单</div>
<div class="code-name">&amp;#xe008;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe009;</span>
<div class="name">安全</div>
<div class="code-name">&amp;#xe009;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe010;</span>
<div class="name">总保费</div>
<div class="code-name">&amp;#xe010;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe011;</span>
<div class="name">斜线</div>
<div class="code-name">&amp;#xe011;</div>
</li>
</ul>
<div class="article markdown">
<h2 id="unicode-">Unicode 引用</h2>
<hr>
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
<ul>
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
</ul>
<blockquote>
<p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
</blockquote>
<p>Unicode 使用步骤如下:</p>
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1757311484908') format('woff2'),
url('iconfont.woff?t=1757311484908') format('woff'),
url('iconfont.ttf?t=1757311484908') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
<pre><code class="language-css"
>.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
<pre>
<code class="language-html"
>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-yiwancheng"></span>
<div class="name">
已完成
</div>
<div class="code-name">.icon-yiwancheng
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-yanqiweiwancheng"></span>
<div class="name">
延期未完成
</div>
<div class="code-name">.icon-yanqiweiwancheng
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-daoru"></span>
<div class="name">
导入
</div>
<div class="code-name">.icon-daoru
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-xuqi"></span>
<div class="name">
续期
</div>
<div class="code-name">.icon-xuqi
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-genjinjilu"></span>
<div class="name">
跟进记录
</div>
<div class="code-name">.icon-genjinjilu
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-yuyue"></span>
<div class="name">
预约
</div>
<div class="code-name">.icon-yuyue
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-huabi"></span>
<div class="name">
画笔
</div>
<div class="code-name">.icon-huabi
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-Homehomepagemenu"></span>
<div class="name">
Home, homepage, menu
</div>
<div class="code-name">.icon-Homehomepagemenu
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-daijiesuan"></span>
<div class="name">
待结算
</div>
<div class="code-name">.icon-daijiesuan
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-jianchafudao"></span>
<div class="name">
检查辅导
</div>
<div class="code-name">.icon-jianchafudao
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-youxiaobaodan"></span>
<div class="name">
有效保单
</div>
<div class="code-name">.icon-youxiaobaodan
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-anquan"></span>
<div class="name">
安全
</div>
<div class="code-name">.icon-anquan
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-zongbaofei"></span>
<div class="name">
总保费
</div>
<div class="code-name">.icon-zongbaofei
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-xiexian"></span>
<div class="name">
斜线
</div>
<div class="code-name">.icon-xiexian
</div>
</li>
</ul>
<div class="article markdown">
<h2 id="font-class-">font-class 引用</h2>
<hr>
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
<p>与 Unicode 使用方式相比,具有如下特点:</p>
<ul>
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="iconfont icon-xxx"&gt;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-yiwancheng"></use>
</svg>
<div class="name">已完成</div>
<div class="code-name">#icon-yiwancheng</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-yanqiweiwancheng"></use>
</svg>
<div class="name">延期未完成</div>
<div class="code-name">#icon-yanqiweiwancheng</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-daoru"></use>
</svg>
<div class="name">导入</div>
<div class="code-name">#icon-daoru</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-xuqi"></use>
</svg>
<div class="name">续期</div>
<div class="code-name">#icon-xuqi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-genjinjilu"></use>
</svg>
<div class="name">跟进记录</div>
<div class="code-name">#icon-genjinjilu</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-yuyue"></use>
</svg>
<div class="name">预约</div>
<div class="code-name">#icon-yuyue</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-huabi"></use>
</svg>
<div class="name">画笔</div>
<div class="code-name">#icon-huabi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-Homehomepagemenu"></use>
</svg>
<div class="name">Home, homepage, menu</div>
<div class="code-name">#icon-Homehomepagemenu</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-daijiesuan"></use>
</svg>
<div class="name">待结算</div>
<div class="code-name">#icon-daijiesuan</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-jianchafudao"></use>
</svg>
<div class="name">检查辅导</div>
<div class="code-name">#icon-jianchafudao</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-youxiaobaodan"></use>
</svg>
<div class="name">有效保单</div>
<div class="code-name">#icon-youxiaobaodan</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-anquan"></use>
</svg>
<div class="name">安全</div>
<div class="code-name">#icon-anquan</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-zongbaofei"></use>
</svg>
<div class="name">总保费</div>
<div class="code-name">#icon-zongbaofei</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-xiexian"></use>
</svg>
<div class="name">斜线</div>
<div class="code-name">#icon-xiexian</div>
</li>
</ul>
<div class="article markdown">
<h2 id="symbol-">Symbol 引用</h2>
<hr>
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
<ul>
<li>支持多色图标了,不再受单色限制。</li>
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
&lt;/style&gt;
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
&lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</code></pre>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.tab-container .content:first').show()
$('#tabs li').click(function (e) {
var tabContent = $('.tab-container .content')
var index = $(this).index()
if ($(this).hasClass('active')) {
return
} else {
$('#tabs li').removeClass('active')
$(this).addClass('active')
tabContent.hide().eq(index).fadeIn()
}
})
})
</script>
</body>
</html>
@font-face {
font-family: "iconfont"; /* Project id 5015061 */
src: url('iconfont.woff2?t=1757311484908') format('woff2'),
url('iconfont.woff?t=1757311484908') format('woff'),
url('iconfont.ttf?t=1757311484908') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-yiwancheng:before {
content: "\e114";
}
.icon-yanqiweiwancheng:before {
content: "\e113";
}
.icon-daoru:before {
content: "\e112";
}
.icon-xuqi:before {
content: "\e001";
}
.icon-genjinjilu:before {
content: "\e002";
}
.icon-yuyue:before {
content: "\e003";
}
.icon-huabi:before {
content: "\e004";
}
.icon-Homehomepagemenu:before {
content: "\e005";
}
.icon-daijiesuan:before {
content: "\e006";
}
.icon-jianchafudao:before {
content: "\e007";
}
.icon-youxiaobaodan:before {
content: "\e008";
}
.icon-anquan:before {
content: "\e009";
}
.icon-zongbaofei:before {
content: "\e010";
}
.icon-xiexian:before {
content: "\e011";
}
window._iconfont_svg_string_5015061='<svg><symbol id="icon-yiwancheng" viewBox="0 0 1024 1024"><path d="M438.5792 716.54968889c-7.54346667 0-15.13244445-2.53724445-21.39022222-7.74826667l-146.65955556-122.22008889c-14.18808889-11.82151111-16.09955555-32.91591111-4.27804444-47.11537778 11.82151111-14.16533333 32.89315555-16.11093333 47.11537777-4.27804444l146.65955556 122.22008889c14.18808889 11.82151111 16.09955555 32.91591111 4.27804444 47.11537778-6.62186667 7.93031111-16.13368889 12.02631111-25.72515555 12.02631111z" ></path><path d="M438.94328889 716.42453333c-8.55608889 0-17.11217778-3.26542222-23.63164444-9.78488888-13.07306667-13.05031111-13.07306667-34.23573333-0.02275556-47.29742223l292.97777778-293.19395555c13.06168889-13.08444445 34.25848889-13.06168889 47.29742222-0.02275556 13.07306667 13.05031111 13.07306667 34.23573333 0.02275556 47.29742222l-292.97777778 293.19395556a33.36647111 33.36647111 0 0 1-23.66577778 9.80764444z" ></path><path d="M512 985.16764445C250.89137778 985.16764445 38.45688889 772.90382222 38.45688889 512S250.89137778 38.83235555 512 38.83235555 985.54311111 251.09617778 985.54311111 512 773.10862222 985.16764445 512 985.16764445z m0-879.44533334c-224.22186667 0-406.64177778 182.24924445-406.64177778 406.27768889S287.77813333 918.27768889 512 918.27768889 918.64177778 736.02844445 918.64177778 512 736.22186667 105.72231111 512 105.72231111z" ></path></symbol><symbol id="icon-yanqiweiwancheng" viewBox="0 0 1024 1024"><path d="M618.54933333 70.784c7.62666667 5.58933333 5.088 9.152-0.512 16.77866667l-68.64 52.37333333s-2.53866667 3.56266667-6.10133333 0.512c-6.10133333 0.512-7.62666667-5.58933333-11.18933333-8.13866667l1.52533333-103.22133333 2.53866667-3.56266667c2.03733333-10.16533333 5.58933333-7.62666667 9.152-5.088 0.01066667 0.52266667 73.22666667 50.34666667 73.22666666 50.34666667z" fill="#333333" ></path><path d="M899.72266667 702.79466667c-69.152 133.728-210.496 224.23466667-372.192 220.672-216.59733333-5.088-393.03466667-181.51466667-398.12266667-398.12266667-5.58933333-229.824 178.976-417.952 407.78666667-417.952V57.568c-285.248 0-509.472 260.33066667-447.44533334 556.75733333 36.608 174.912 174.4 312.704 349.312 349.312 217.10933333 45.248 414.90133333-63.552 505.408-236.93866666l-44.74666666-23.904zM722.272 91.12533333c9.152 4.064 18.304 8.13866667 26.944 12.71466667 13.728 7.11466667 17.792 25.41866667 7.62666667 37.12l-0.512 0.512c-7.62666667 9.152-20.34133333 11.18933333-30.50666667 5.58933333-7.62666667-4.576-14.74133333-8.13866667-22.88-11.69066666-10.67733333-4.576-16.26666667-15.76533333-14.24-27.456 3.06133333-14.76266667 19.328-22.89066667 33.568-16.78933334z m118.47466667 75.75466667c8.64 7.62666667 16.77866667 15.25333333 24.91733333 23.89333333 9.152 9.664 8.64 24.91733333-1.01333333 34.06933334l-1.01333334 1.01333333c-9.664 9.152-25.41866667 8.64-34.57066666-1.52533333-6.61333333-7.62666667-13.728-14.74133333-20.84266667-21.35466667-8.64-8.13866667-10.16533333-21.35466667-3.56266667-31.52 8.62933333-12.20266667 25.408-14.24 36.08533334-4.576zM935.31733333 340.26666667c-12.20266667 5.58933333-26.44266667 0-32.544-12.20266667-4.576-8.64-9.152-17.29066667-14.24-25.93066667-6.10133333-10.16533333-4.064-22.88 4.064-31.01866666 11.69066667-11.18933333 30.50666667-8.13866667 38.64533334 5.58933333 5.58933333 9.664 11.18933333 19.82933333 16.26666666 29.49333333 6.61333333 12.71466667 1.024 27.968-12.192 34.06933334z m31.52 125.58933333c-12.71466667 0.512-23.89333333-8.13866667-25.93066666-20.84266667-1.52533333-10.16533333-3.56266667-20.84266667-6.10133334-31.01866666-3.05066667-11.69066667 3.05066667-24.40533333 14.24-28.98133334 14.24-6.10133333 30.50666667 2.03733333 34.06933334 16.77866667 3.05066667 11.69066667 5.088 22.88 7.11466666 34.57066667 2.53866667 15.25333333-8.64 28.98133333-23.392 29.49333333z m-7.11466666 129.14133333c-11.69066667-3.56266667-19.31733333-15.25333333-17.29066667-27.456 1.52533333-10.16533333 2.53866667-20.84266667 3.05066667-31.01866666 1.01333333-12.71466667 11.18933333-22.368 23.89333333-22.88 14.74133333-0.512 26.944 11.69066667 25.93066667 26.944-0.512 11.69066667-2.03733333 22.88-3.56266667 34.57066666-2.528 14.25066667-17.78133333 24.416-32.02133333 19.84z" fill="#333333" ></path><path d="M896.16 712.96c0 14.32533333 11.60533333 25.93066667 25.93066667 25.94133333 14.32533333 0 25.93066667-11.60533333 25.94133333-25.93066666v-0.01066667c0-14.32533333-11.60533333-25.93066667-25.93066667-25.94133333-14.32533333 0-25.93066667 11.60533333-25.94133333 25.93066666v0.01066667zM540.24533333 553.81333333c-15.76533333 0-28.98133333-12.71466667-28.98133333-28.98133333v-122.02666667c0-15.76533333 12.71466667-28.98133333 28.98133333-28.98133333 15.76533333 0 28.98133333 12.71466667 28.98133334 28.98133333v122.02666667c0 16.27733333-13.216 28.98133333-28.98133334 28.98133333z" fill="#333333" ></path><path d="M760.91733333 729.23733333c-11.18933333 12.20266667-29.99466667 12.71466667-41.696 1.52533334L521.42933333 545.68533333c-12.20266667-11.18933333-12.71466667-29.99466667-1.52533333-41.696 11.18933333-12.20266667 29.99466667-12.71466667 41.696-1.52533333l197.792 185.07733333c11.69066667 10.67733333 12.704 29.49333333 1.52533333 41.696z" fill="#333333" ></path></symbol><symbol id="icon-daoru" viewBox="0 0 1024 1024"><path d="M603.886933 0c17.6128 0 32.221867 5.3248 45.056 15.837867l6.280534 5.666133 222.890666 220.842667c13.038933 11.946667 20.616533 26.624 22.528 43.690666l0.477867 8.874667-0.068267 182.8864h-81.988266l0.068266-140.151467H669.764267a102.4 102.4 0 0 1-101.9904-92.501333l-0.4096-9.8304-0.068267-153.463467-361.8816 0.068267c-29.696 0-51.6096 19.387733-54.818133 46.830933l-0.4096 6.9632v697.412267c0 28.945067 19.8656 50.722133 47.991466 53.930667L205.482667 887.466667H546.133333v81.92H205.4144C131.959467 969.386667 73.728 915.2512 68.608 843.502933L68.266667 833.1264V135.714133C68.266667 62.6688 122.88 5.461333 194.9696 0.341333L205.482667 0h398.472533z m69.632 543.9488c22.254933 0 40.3456 19.114667 40.3456 42.666667v1.024h0.2048v57.480533h222.208v139.0592H714.069333v55.978667h-0.341333l0.136533 2.4576c0 23.552-18.090667 42.666667-40.413866 42.666666a38.980267 38.980267 0 0 1-26.965334-11.0592l-134.826666-127.863466a44.100267 44.100267 0 0 1 0.136533-63.2832l134.621867-128.2048a40.004267 40.004267 0 0 1 27.0336-10.922667zM635.562667 117.418667v117.9648a34.133333 34.133333 0 0 0 27.989333 33.5872l6.144 0.546133 119.330133-0.068267L635.562667 117.3504z" fill="#999999" ></path></symbol><symbol id="icon-xuqi" viewBox="0 0 1024 1024"><path d="M472.759124 545.884185c-4.110949-4.858394-6.602433-11.336253-6.477859-17.564964l1.993188-296.984914c0.373723-14.948905 12.457421-26.783455 27.5309-26.409733 14.948905 0.373723 26.783455 12.457421 26.409732 27.5309L519.972749 505.77129l258.117762 155.96691c14.824331 1.993187 24.914842 15.820925 22.921654 30.396107-1.993187 14.824331-15.820925 24.914842-30.396107 22.921654L488.953771 555.227251c-6.22871-0.996594-12.083698-4.360097-16.194647-9.343066z m158.084672 40.23747" fill="" ></path><path d="M511.501703 1022.006813C230.088564 1022.006813 1.245742 793.16399 1.245742 511.626277 1.245742 230.337713 230.088564 1.494891 511.501703 1.494891c34.63163 0 69.014112 3.612652 103.022871 10.588807C629.099757 14.948905 638.567397 29.025791 635.577616 43.600973c-2.865207 14.575182-16.942092 24.042822-31.517275 21.053042-29.89781-6.477859-60.916788-9.46764-91.811193-9.46764-251.639903 0.124574-456.439903 204.924574-456.439902 456.564477 0 251.764477 204.924574 456.439903 456.439902 456.439902s456.439903-204.924574 456.439903-456.439902c0-58.549878-11.087105-115.854015-32.763017-169.670073-5.232117-13.952311 1.121168-29.274939 15.322628-35.129927 13.454015-5.232117 29.274939 1.121168 35.129927 15.322627 24.042822 60.293917 36.251095 123.951338 36.251095 189.601947-0.872019 280.790268-229.590268 510.131387-511.127981 510.131387z m0 0" fill="" ></path><path d="M942.777616 170.043796H729.132847c-14.575182 0-26.908029-12.332847-26.908029-26.90803 0-14.575182 12.332847-26.908029 26.908029-26.908029h213.644769c14.575182 0 26.908029 12.332847 26.908029 26.908029-0.124574 14.575182-12.208273 26.908029-26.908029 26.90803z m0 0" fill="" ></path><path d="M862.925547 36.375669v213.644769c0 14.575182-12.332847 26.908029-26.908029 26.908029-14.575182 0-26.908029-12.332847-26.908029-26.908029V36.375669c0-14.575182 12.332847-26.908029 26.908029-26.908029 14.575182 0 26.908029 12.208273 26.908029 26.908029z m0 0" fill="" ></path></symbol><symbol id="icon-genjinjilu" viewBox="0 0 1024 1024"><path d="M754.688 584.81777778c-120.60444445 0-218.45333333 97.84888889-218.45333333 218.45333333s97.84888889 218.45333333 218.45333333 218.45333334 218.45333333-97.84888889 218.45333333-218.45333334-97.73511111-218.45333333-218.45333333-218.45333333z m0 388.32355555C660.93511111 973.14133333 584.81777778 897.13777778 584.81777778 803.27111111c0-93.86666667 76.11733333-169.87022222 169.87022222-169.87022222S924.672 709.40444445 924.672 803.27111111c0 93.86666667-76.11733333 169.87022222-169.984 169.87022222z" fill="#000000" ></path><path d="M924.672 2.27555555H99.328c-26.73777778 0-48.58311111 21.84533333-48.58311111 48.58311112v922.39644444c0 26.73777778 21.84533333 48.58311111 48.58311111 48.58311111H475.59111111c13.42577778 0 24.23466667-10.92266667 24.23466667-24.23466667 0-13.42577778-10.92266667-24.23466667-24.23466667-24.23466666H123.67644445c-13.312 0-24.23466667-10.92266667-24.23466667-24.23466667V75.09333333c0-13.312 10.92266667-24.23466667 24.23466667-24.23466666h776.76088888c13.312 0 24.23466667 10.92266667 24.23466667 24.23466666v436.90666667c0 13.42577778 10.92266667 24.23466667 24.23466667 24.23466667s24.23466667-10.92266667 24.23466666-24.23466667V50.85866667c0-26.73777778-21.84533333-48.58311111-48.46933333-48.58311112z" fill="#000000" ></path><path d="M779.03644445 817.49333333V730.45333333c0-13.312-10.92266667-24.23466667-24.23466667-24.23466666-13.312 0-24.23466667 10.92266667-24.23466667 24.23466666v97.05244445c0 0.22755555 0.11377778 0.34133333 0.11377778 0.56888889 0.11377778 2.95822222 0.56888889 5.91644445 1.82044444 8.64711111 0.68266667 1.70666667 1.93422222 3.072 3.072 4.55111111 0.79644445 1.024 1.25155555 2.27555555 2.16177778 3.29955556l43.57688889 43.57688888c9.44355555 9.44355555 24.91733333 9.44355555 34.36088889 0s9.44355555-24.91733333 0-34.36088888l-36.63644444-36.29511112zM269.312 293.54666667h485.48977778c13.312 0 24.23466667-10.92266667 24.23466667-24.23466667 0-13.312-10.92266667-24.23466667-24.23466667-24.23466667H269.312c-13.312 0-24.23466667 10.92266667-24.23466667 24.23466667-0.11377778 13.312 10.80888889 24.23466667 24.23466667 24.23466667zM779.03644445 439.18222222c0-13.312-10.92266667-24.23466667-24.23466667-24.23466667H269.312c-13.312 0-24.23466667 10.92266667-24.23466667 24.23466667s10.92266667 24.23466667 24.23466667 24.23466667h485.48977778c13.312 0 24.23466667-10.92266667 24.23466667-24.23466667zM269.312 584.81777778c-13.312 0-24.23466667 10.92266667-24.23466667 24.23466667 0 13.312 10.92266667 24.23466667 24.23466667 24.23466666h194.21866667c13.312 0 24.23466667-10.92266667 24.23466666-24.23466666 0-13.312-10.92266667-24.23466667-24.23466666-24.23466667H269.312z" fill="#000000" ></path></symbol><symbol id="icon-yuyue" viewBox="0 0 1028 1024"><path d="M955.392 982.016h-882.688c-39.936 0-72.704-32.768-72.704-72.704v-259.072l26.112 0.512h2.56c75.264-0.512 136.704-62.464 136.704-138.24 0-76.288-61.44-138.24-136.704-138.24h-2.56l-26.112 0.512v-259.072c0-39.936 32.768-72.704 72.704-72.704h882.688c39.936 0 72.704 32.768 72.704 72.704v258.56h-25.6c-75.776 0-137.216 61.952-137.216 138.24s61.44 138.24 137.216 138.24h25.6v258.56c0 39.936-32.768 72.704-72.704 72.704zM51.2 699.904v208.896c0 11.776 9.728 21.504 21.504 21.504h882.688c11.776 0 21.504-9.728 21.504-21.504V699.904c-91.648-12.8-162.816-92.16-162.816-187.904 0-95.744 71.168-175.104 162.816-187.904v-209.408c0-11.776-9.728-21.504-21.504-21.504h-882.688c-11.776 0-21.504 9.728-21.504 21.504v208.896c93.184 11.264 165.376 91.648 165.376 188.416s-72.192 176.64-165.376 187.904z" fill="#012733" ></path><path d="M673.792 799.744h-319.488c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h319.488c14.336 0 25.6 11.264 25.6 25.6s-11.776 25.6-25.6 25.6zM673.792 537.6h-112.64c-39.936 0-72.704-32.768-72.704-72.704v-242.688c0-14.336 11.264-25.6 25.6-25.6s25.6 11.264 25.6 25.6V464.384c0 11.776 9.728 21.504 21.504 21.504h112.64c14.336 0 25.6 11.264 25.6 25.6s-11.776 26.112-25.6 26.112z" fill="#012733" ></path></symbol><symbol id="icon-huabi" viewBox="0 0 1024 1024"><path d="M777.96693333 350.27626667l-513.13777778 611.5328a21.26848 21.26848 0 0 1-9.6256 6.53084444l-155.39768888 51.5072c-15.06417778 4.99484445-30.1056-7.63448889-27.79591112-23.32444444l23.73404445-161.97404445c0.56888889-3.91395555 2.21866667-7.58897778 4.76728889-10.61546667l513.13777778-611.5328c7.56622222-9.02257778 21.01475555-10.19448889 30.03733333-2.62826666l131.65226667 110.47822222c9.0112 7.55484445 10.19448889 21.00337778 2.62826666 30.02595556zM947.23413333 148.54826667l-74.97955555 89.34968888c-7.56622222 9.02257778-21.01475555 10.19448889-30.03733333 2.62826667l-131.65226667-110.47822222c-9.02257778-7.56622222-10.19448889-21.01475555-2.62826667-30.03733333l74.97955556-89.34968889c7.56622222-9.02257778 21.01475555-10.19448889 30.03733333-2.62826667l131.65226667 110.47822222c9.02257778 7.56622222 10.19448889 21.01475555 2.62826666 30.03733334z" fill="#6C6D6E" ></path></symbol><symbol id="icon-Homehomepagemenu" viewBox="0 0 1024 1024"><path d="M952.14301275 978.03377778H641.45382756a25.89076502 25.89076502 0 0 1-25.89076624-25.89076503V667.34459259h-207.12612264v284.79842016a25.89076502 25.89076502 0 0 1-25.89076624 25.89076503H71.85698725a25.89076502 25.89076502 0 0 1-25.89076503-25.89076503V382.54617244a25.94254697 25.94254697 0 0 1 10.95179378-21.15275496l440.14301275-310.68918518a25.91665661 25.91665661 0 0 1 29.8779425 0l440.14301275 310.68918518A25.94254697 25.94254697 0 0 1 978.03377778 382.54617244v569.59684031a25.89076502 25.89076502 0 0 1-25.89076503 25.89076503z m-284.79842016-51.78153127h258.90765392V395.98347969L512 103.54728414 97.74775349 395.98347969V926.25224651h258.90765392V641.45382756a25.89076502 25.89076502 0 0 1 25.89076503-25.89076624h258.90765512a25.89076502 25.89076502 0 0 1 25.89076503 25.89076624z" fill="#646464" ></path></symbol><symbol id="icon-daijiesuan" viewBox="0 0 1024 1024"><path d="M828.53888 837.27701333l-155.32032-199.66634666a32.65877333 32.65877333 0 1 1 51.55498667-40.19541334l99.61472 128.01365334L908.4928 599.38133333a32.768 32.768 0 1 1 54.61333333 36.26325334l-134.56725333 201.63242666z" fill="#26BEC9" ></path><path d="M953.93109333 813.90250667H698.99605333c-18.13162667 0-32.768-14.63637333-32.768-32.768s14.63637333-32.768 32.768-32.768h254.93504c18.13162667 0 32.768 14.63637333 32.768 32.768s-14.63637333 32.768-32.768 32.768zM953.93109333 923.12917333H698.99605333c-18.13162667 0-32.768-14.63637333-32.768-32.768s14.63637333-32.768 32.768-32.768h254.93504c18.13162667 0 32.768 14.63637333 32.768 32.768s-14.63637333 32.768-32.768 32.768z" fill="#26BEC9" ></path><path d="M826.5728 1032.35584c-18.13162667 0-32.768-14.63637333-32.768-32.768v-218.45333333c0-18.13162667 14.63637333-32.768 32.768-32.768s32.768 14.63637333 32.768 32.768v218.45333333c0 18.13162667-14.63637333 32.768-32.768 32.768z" fill="#26BEC9" ></path><path d="M520.73813333 1032.35584c-283.11552 0-513.36533333-230.24981333-513.36533333-513.36533333s230.24981333-513.36533333 513.36533333-513.36533334c268.6976 0 493.48608 209.7152 512.05461334 477.32053334a32.768 32.768 0 0 1-30.36501334 34.95253333c-18.13162667 1.09226667-33.64181333-12.45184-34.95253333-30.36501333C951.30965333 254.22506667 755.13856 71.16117333 520.73813333 71.16117333c-246.85226667 0-447.82933333 200.97706667-447.82933333 447.82933334s200.97706667 447.82933333 447.82933333 447.82933333c24.90368 0 50.02581333-1.96608 74.27413334-6.11669333 17.91317333-3.05834667 34.73408 9.17504 37.79242666 26.86976s-9.17504 34.73408-26.86976 37.79242666c-27.96202667 4.80597333-56.57941333 6.99050667-85.1968 6.99050667z" fill="#2C2C2C" ></path><path d="M347.06773333 737.44384c-10.70421333 0-21.18997333-5.24288-27.30666666-14.85482667-10.04885333-15.07328-5.67978667-35.38944 9.39349333-45.43829333l174.98112-115.12490667V233.03509333c0-18.13162667 14.63637333-32.768 32.768-32.768s32.768 14.63637333 32.768 32.768v364.59861334l-204.47232 134.56725333c-5.67978667 3.71370667-12.01493333 5.24288-18.13162667 5.24288z" fill="#2C2C2C" ></path></symbol><symbol id="icon-jianchafudao" viewBox="0 0 1024 1024"><path d="M861.90762667 969.60512H162.03776a26.92437333 26.92437333 0 0 1-26.86976-26.92437333v-53.79413334a26.92437333 26.92437333 0 0 1 26.86976-26.92437333h699.86986667a26.97898667 26.97898667 0 0 1 26.92437333 26.92437333v53.79413334a26.97898667 26.97898667 0 0 1-26.92437333 26.92437333z m107.69749333-161.54624H54.39488a53.90336 53.90336 0 0 1-53.84874667-53.79413333V108.18901333a53.95797333 53.95797333 0 0 1 53.84874667-53.90336h915.21024a53.90336 53.90336 0 0 1 53.79413333 53.90336v646.07573334a53.84874667 53.84874667 0 0 1-53.79413333 53.79413333z m-616.69376-140.9024a27.30666667 27.30666667 0 0 0-27.30666667 27.30666667 27.30666667 27.30666667 0 0 0 27.30666667 27.30666666h318.23189333a27.30666667 27.30666667 0 0 0 27.30666667-27.30666666 27.30666667 27.30666667 0 0 0-27.30666667-27.30666667z m227.02762667-211.08053333c0.70997333 0.92842667 10.48576 10.92266667 28.34432 28.89045333l1.74762666 1.74762667-1.6384 1.6384a14.36330667 14.36330667 0 0 0 0 20.15232l60.40234667 60.40234666a14.36330667 14.36330667 0 0 0 10.10346667 4.20522667 14.19946667 14.19946667 0 0 0 10.04885333-4.20522667l30.20117333-30.25578666a13.9264 13.9264 0 0 0 4.20522667-10.04885334 13.9264 13.9264 0 0 0-4.20522667-10.04885333l-60.40234666-60.40234667a13.98101333 13.98101333 0 0 0-10.04885334-4.20522666 14.19946667 14.19946667 0 0 0-10.04885333 4.20522666l-1.6384 1.6384a2176.66901333 2176.66901333 0 0 0-30.69269333-30.20117333 169.79285333 169.79285333 0 0 0 37.79242666-107.47904 171.92277333 171.92277333 0 0 0-171.75893333-171.70432 171.92277333 171.92277333 0 0 0-171.75893333 171.70432 171.97738667 171.97738667 0 0 0 171.75893333 171.81354667 172.14122667 172.14122667 0 0 0 107.47904-37.79242667z m-107.53365334-28.34432a105.73141333 105.73141333 0 0 1-105.56757333-105.62218667 105.6768 105.6768 0 0 1 105.56757333-105.51296 105.6768 105.6768 0 0 1 105.62218667 105.51296 105.73141333 105.73141333 0 0 1-105.62218667 105.56757333z" fill="#333333" ></path></symbol><symbol id="icon-youxiaobaodan" viewBox="0 0 1024 1024"><path d="M869.8144237 1013.42321778H159.33136592c-59.91689482 0-108.66839703-48.75150222-108.66839703-108.66839703V121.44184889c0-59.91689482 48.75150222-108.66839703 108.66839703-108.66839704h710.49519408c59.91689482 0 108.66839703 48.75150222 108.66839703 108.66839704v783.31297186c0 59.91689482-48.75150222 108.66839703-108.68053333 108.66839703zM159.33136592 73.45493333c-26.45712592 0-47.98691555 21.52978963-47.98691555 47.98691556v783.31297186c0 26.45712592 21.52978963 47.98691555 47.98691555 47.98691555h710.49519408c26.45712592 0 47.98691555-21.52978963 47.98691555-47.98691555V121.44184889c0-26.45712592-21.52978963-47.98691555-47.98691555-47.98691556H159.33136592z" fill="#333333" ></path><path d="M566.77110518 830.65059555c-6.65069037 0-13.33778963-2.17239703-18.93262221-6.65069037l-130.55013927-104.44496593c-13.08292741-10.4736237-15.20677925-29.56401778-4.73315555-42.64694517 10.46148741-13.08292741 29.56401778-15.20677925 42.64694518-4.73315556l106.86008889 85.48807111 189.93303703-237.41022815c10.4736237-13.08292741 29.56401778-15.20677925 42.6469452-4.73315556 13.08292741 10.4736237 15.20677925 29.56401778 4.73315555 42.64694519L590.48542815 819.25461333c-5.99533037 7.50023111-14.80628148 11.39598222-23.71432297 11.39598222zM775.67317333 308.43790222H253.4726163c-16.76022518 0-30.34074075-13.58051555-30.34074075-30.34074074s13.58051555-30.34074075 30.34074075-30.34074073h522.21269333c16.76022518 0 30.34074075 13.58051555 30.34074074 30.34074073s-13.59265185 30.34074075-30.35287704 30.34074074zM462.35041185 517.32783408H253.4726163c-16.76022518 0-30.34074075-13.58051555-30.34074075-30.34074075s13.58051555-30.34074075 30.34074075-30.34074074h208.87779555c16.76022518 0 30.34074075 13.58051555 30.34074074 30.34074074s-13.58051555 30.34074075-30.34074074 30.34074075z" fill="#333333" ></path></symbol><symbol id="icon-anquan" viewBox="0 0 1024 1024"><path d="M512 998.4a78.29333333 78.29333333 0 0 1-48.27733333-16.68266667l-101.26933334-79.21066666A590.18666667 590.18666667 0 0 1 136.02133333 437.76v-224a62.76266667 62.76266667 0 0 1 54.208-62.05866667c195.49866667-26.176 271.59466667-92.77866667 290.26133334-112.576a44.416 44.416 0 0 1 61.632-3.05066666c26.368 22.93333333 125.22666667 99.09333333 290.13333333 116.352a62.31466667 62.31466667 0 0 1 55.76533333 62.272V437.76A590.18666667 590.18666667 0 0 1 661.54666667 902.50666667l-101.26933334 79.25333333A78.29333333 78.29333333 0 0 1 512 998.4zM204.288 218.624V437.76a518.69866667 518.69866667 0 0 0 200.256 410.98666667L505.81333333 928a10.02666667 10.02666667 0 0 0 12.39466667 0l101.248-79.25333333a518.69866667 518.69866667 0 0 0 200.256-410.98666667V219.73333333C662.976 202.02666667 558.93333333 135.744 514.13333333 101.22666667c-40.34133333 35.05066667-131.17866667 92.62933333-309.84533333 117.39733333z" ></path><path d="M488.53333333 636.224h-0.49066666a34.13333333 34.13333333 0 0 1-24.32-10.66666667l-126.35733334-133.696a34.13333333 34.13333333 0 1 1 49.62133334-46.93333333l102.22933333 108.18133333 187.37066667-187.37066666a34.13333333 34.13333333 0 0 1 48.27733333 48.256l-212.224 212.224a34.13333333 34.13333333 0 0 1-24.10666667 10.00533333z" ></path></symbol><symbol id="icon-zongbaofei" viewBox="0 0 1163 1024"><path d="M558.545 446.836c23.273-27.927 51.2-51.2 79.128-69.818v-51.2c0-37.236-32.582-69.818-69.818-69.818H69.818C32.582 260.655 0 288.582 0 325.818v51.2c0 41.891 32.582 69.818 69.818 69.818h488.727zM74.473 572.51v51.2c0 37.236 32.582 69.818 69.818 69.818h325.818c0-69.818 18.618-134.982 51.2-186.182H144.291c-37.236-4.654-69.818 27.928-69.818 65.164z m69.818-372.364h502.69c37.237 0 69.819-32.581 69.819-69.818v-51.2c0-37.236-32.582-69.818-69.818-69.818H144.29c-37.236 4.655-69.818 37.236-69.818 74.473v51.2c0 37.236 32.582 65.163 69.818 65.163z m-37.236 549.237c-37.237 0-69.819 32.582-69.819 69.818v51.2c0 37.236 32.582 69.818 69.819 69.818h451.49c-46.545-51.2-74.472-116.363-83.781-186.182h-367.71z m735.418-377.018c-176.873 0-321.164 144.29-321.164 321.163 0 176.873 144.291 321.164 321.164 321.164 176.872 0 321.163-144.291 321.163-321.164 0-176.872-144.29-321.163-321.163-321.163zM972.8 744.727c13.964 0 23.273 9.31 23.273 23.273s-9.31 23.273-23.273 23.273h-88.436v51.2c0 18.618-13.964 32.582-32.582 32.582-18.618 0-32.582-13.964-32.582-32.582v-51.2h-88.436c-13.964 0-23.273-9.31-23.273-23.273s9.309-23.273 23.273-23.273H819.2v-32.582h-88.436c-13.964 0-23.273-9.309-23.273-23.272S716.8 665.6 730.764 665.6H819.2v-27.927l-88.436-88.437c-9.31-9.309-9.31-27.927 0-37.236 9.309-9.31 27.927-9.31 41.89 0l83.782 79.127L930.91 512c9.31-9.31 27.927-9.31 41.891 0 9.31 9.31 13.964 27.927 0 37.236l-88.436 88.437V665.6H972.8c13.964 0 23.273 9.31 23.273 23.273s-9.31 23.272-23.273 23.272h-88.436v32.582H972.8z" ></path></symbol><symbol id="icon-xiexian" viewBox="0 0 1024 1024"><path d="M616.12942222 34.16974222h126.84856889L407.87057778 989.83025778H281.02200889L616.12942222 34.16974222z" ></path></symbol></svg>',(t=>{var c=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var a,i,e,h,o,n=function(c,l){l.parentNode.insertBefore(c,l)};if(c&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(c){console&&console.log(c)}}a=function(){var c,l=document.createElement("div");l.innerHTML=t._iconfont_svg_string_5015061,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(c=document.body).firstChild?n(l,c.firstChild):c.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(i=function(){document.removeEventListener("DOMContentLoaded",i,!1),a()},document.addEventListener("DOMContentLoaded",i,!1)):document.attachEvent&&(e=a,h=t.document,o=!1,d(),h.onreadystatechange=function(){"complete"==h.readyState&&(h.onreadystatechange=null,s())})}function s(){o||(o=!0,e())}function d(){try{h.documentElement.doScroll("left")}catch(c){return void setTimeout(d,50)}s()}})(window);
\ No newline at end of file
{
"id": "5015061",
"name": "csf",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "csf核心系统",
"glyphs": [
{
"icon_id": "8300842",
"name": "已完成",
"font_class": "yiwancheng",
"unicode": "e114",
"unicode_decimal": 57620
},
{
"icon_id": "16725409",
"name": "延期未完成",
"font_class": "yanqiweiwancheng",
"unicode": "e113",
"unicode_decimal": 57619
},
{
"icon_id": "23191146",
"name": "导入",
"font_class": "daoru",
"unicode": "e112",
"unicode_decimal": 57618
},
{
"icon_id": "6079882",
"name": "续期",
"font_class": "xuqi",
"unicode": "e001",
"unicode_decimal": 57345
},
{
"icon_id": "8387742",
"name": "跟进记录",
"font_class": "genjinjilu",
"unicode": "e002",
"unicode_decimal": 57346
},
{
"icon_id": "9550464",
"name": "预约",
"font_class": "yuyue",
"unicode": "e003",
"unicode_decimal": 57347
},
{
"icon_id": "10048834",
"name": "画笔",
"font_class": "huabi",
"unicode": "e004",
"unicode_decimal": 57348
},
{
"icon_id": "11982657",
"name": "Home, homepage, menu",
"font_class": "Homehomepagemenu",
"unicode": "e005",
"unicode_decimal": 57349
},
{
"icon_id": "20438891",
"name": "待结算",
"font_class": "daijiesuan",
"unicode": "e006",
"unicode_decimal": 57350
},
{
"icon_id": "27966554",
"name": "检查辅导",
"font_class": "jianchafudao",
"unicode": "e007",
"unicode_decimal": 57351
},
{
"icon_id": "31367111",
"name": "有效保单",
"font_class": "youxiaobaodan",
"unicode": "e008",
"unicode_decimal": 57352
},
{
"icon_id": "34180715",
"name": "安全",
"font_class": "anquan",
"unicode": "e009",
"unicode_decimal": 57353
},
{
"icon_id": "38352588",
"name": "总保费",
"font_class": "zongbaofei",
"unicode": "e010",
"unicode_decimal": 57360
},
{
"icon_id": "42202603",
"name": "斜线",
"font_class": "xiexian",
"unicode": "e011",
"unicode_decimal": 57361
}
]
}
const customer = [
{
fatherTitle: '客户',
type: 'object',
key: 'personInfo',
data: [
{
label: '姓氏',
key: 'lastName',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 5,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '名字',
key: 'firstName',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 10,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '姓名',
key: 'name',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 15,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '姓氏拼音',
key: 'lastNamePinyin',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 30,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '姓名拼音',
key: 'pinyin',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 30,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '名字-英文',
key: 'firstNamePinyin',
type: 'Input',
inputType: 'text',
required: true,
maxLength: 30,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '称谓',
key: 'title',
type: 'Select',
required: true,
disabled: false,
placeholder: '请选择',
dictType: 'csf_customer_title',
show: true
},
{
label: '性别',
key: 'gender',
type: 'Select',
required: false,
disabled: false,
placeholder: '请选择',
dictType: 'sys_gender',
show: true
},
{
label: '生日',
key: 'birthdate',
type: 'DatePicker',
required: false,
disabled: false,
placeholder: '请选择',
show: true
},
{
label: '年龄',
key: 'age',
type: 'Input',
inputType: 'number',
maxLength: 30,
required: true,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '移动电话',
key: 'phone',
type: 'arrowRight',
required: true,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'phone',
phone: {},
code: 'areaCode',
value: ''
},
{
label: '邮箱',
key: 'email',
type: 'Input',
inputType: 'text',
maxLength: 30,
required: true,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '是否吸烟',
key: 'smoke',
type: 'Select',
required: false,
disabled: false,
placeholder: '请选择',
dictType: 'sys_no_yes',
show: true
},
{
label: '吸烟数量(支/天)',
key: 'smokeQuantity',
type: 'Input',
inputType: 'number',
maxLength: 30,
required: false,
disabled: false,
placeholder: '请输入',
show: false
},
// {
// label: '公司类型',
// key: 'companyType',
// type: 'Select',
// required: false,
// disabled: false,
// placeholder: '请选择',
// dictType: 'sys_status',
// show: true
// },
{
label: '客户类型',
key: 'customerType',
type: 'Select',
required: false,
disabled: false,
placeholder: '请选择',
dictType: 'csf_customer_type',
show: true
},
// {
// label: '客户来源',
// key: 'source',
// type: 'Select',
// required: false,
// disabled: false,
// placeholder: '请选择',
// dictType: 'sys_status',
// show: true
// },
{
label: '行业',
key: 'companyType',
type: 'Input',
inputType: 'text',
maxLength: 300,
required: true,
disabled: false,
placeholder: '请输入',
show: true
}
]
},
{
fatherTitle: '基本情况',
type: 'object',
key: 'basic',
// description: '证件信息至少填写一项',
data: [
{
label: '婚姻状况',
key: 'marriage',
type: 'Select',
required: true,
disabled: false,
placeholder: '请选择',
dictType: 'csf_marriage',
show: true
},
{
label: '国籍(国家/地区)',
key: 'country',
type: 'arrowRight',
required: true,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'country'
},
{
label: '出生地',
key: 'birthplace',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 300,
disabled: false,
placeholder: '请输入出生地(省市)',
show: true
},
{
label: '教育程度',
key: 'education',
type: 'Select',
required: true,
disabled: false,
placeholder: '请选择',
dictType: 'csf_education',
show: true
},
{
label: '住宅电话',
key: 'residenceTelephone',
type: 'arrowRight',
required: false,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'phone',
residenceTelephone: {},
code: 'residenceAreaCode',
maxLength: 20
},
{
label: '固定电话',
key: 'fixedPhone',
type: 'Input',
inputType: 'number',
required: false,
disabled: false,
placeholder: '请填写',
show: true,
maxLength: 20
},
{
label: '是否长期出国',
key: 'longtimeAbroad',
type: 'Select',
required: false,
disabled: false,
placeholder: '请选择',
dictType: 'sys_no_yes',
show: true
},
{
label: '通讯地址',
key: 'residenceAddress',
type: 'arrowRight',
required: false,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'address',
residenceAddress: {}
},
{
label: '住宅地址',
key: 'residentialAddress',
type: 'arrowRight',
required: false,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'address',
residentialAddress: {}
},
{
label: '邮寄地址',
key: 'mailingAddress',
type: 'arrowRight',
required: false,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'address',
mailingAddress: {}
}
]
},
{
fatherTitle: '证件信息',
type: 'object',
key: 'pid',
description: '证件信息至少填写一项',
data: [
{
label: '证件类型',
key: 'idType',
type: 'Select',
required: true,
disabled: false,
placeholder: '请选择',
dictType: 'csf_id_type',
show: true
},
{
label: '证件号码',
key: 'idCard',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 20,
disabled: false,
placeholder: '请输入',
show: true
}
]
},
{
fatherTitle: '公司信息',
type: 'object',
key: 'companyInfo',
data: [
{
label: '公司名称',
key: 'companyName',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 300,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '公司电话',
key: 'companyTelephone',
type: 'arrowRight',
required: false,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'phone',
companyTelephone: {},
code: 'companyAreaCode',
maxLength: 20
},
{
label: '职位',
key: 'position',
type: 'Input',
inputType: 'text',
required: false,
maxLength: 300,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '总工作年期',
key: 'workYear',
type: 'Input',
inputType: 'number',
required: false,
maxLength: 300,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '现时每月收入',
key: 'salary',
type: 'Input',
inputType: 'number',
required: false,
maxLength: 300,
disabled: false,
placeholder: '请输入',
show: true
},
{
label: '公司地址',
key: 'companyAddress',
type: 'arrowRight',
required: false,
disabled: false,
placeholder: '请填写',
show: true,
drawerType: 'address',
companyAddress: {}
}
]
}
]
export default customer
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<section class="app-main"> <section class="app-main">
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<transition name="fade-transform" mode="out-in"> <transition name="fade-transform" mode="out-in">
<keep-alive :include="tagsViewStore.cachedViews"> <!-- <keep-alive :include="tagsViewStore.cachedViews">
<component v-if="!route.meta.link" :is="Component" :key="route.path"/>
</keep-alive> </keep-alive> -->
<component v-if="!route.meta.link" :is="Component" :key="route.path" />
</transition> </transition>
</router-view> </router-view>
<iframe-toggle /> <iframe-toggle />
...@@ -13,8 +14,8 @@ ...@@ -13,8 +14,8 @@
</template> </template>
<script setup> <script setup>
import copyright from "./Copyright/index" import copyright from './Copyright/index'
import iframeToggle from "./IframeToggle/index" import iframeToggle from './IframeToggle/index'
import useTagsViewStore from '@/store/modules/tagsView' import useTagsViewStore from '@/store/modules/tagsView'
const route = useRoute() const route = useRoute()
...@@ -86,4 +87,3 @@ function addIframe() { ...@@ -86,4 +87,3 @@ function addIframe() {
border-radius: 3px; border-radius: 3px;
} }
</style> </style>
...@@ -45,12 +45,12 @@ ...@@ -45,12 +45,12 @@
<screenfull id="screenfull" class="right-menu-item hover-effect" /> <screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="主题模式" effect="dark" placement="bottom"> <!-- <el-tooltip content="主题模式" effect="dark" placement="bottom">
<div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme"> <div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme">
<svg-icon v-if="settingsStore.isDark" icon-class="sunny" /> <svg-icon v-if="settingsStore.isDark" icon-class="sunny" />
<svg-icon v-if="!settingsStore.isDark" icon-class="moon" /> <svg-icon v-if="!settingsStore.isDark" icon-class="moon" />
</div> </div>
</el-tooltip> </el-tooltip> -->
<el-tooltip content="布局大小" effect="dark" placement="bottom"> <el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" /> <size-select id="size-select" class="right-menu-item hover-effect" />
...@@ -162,7 +162,11 @@ function logout() { ...@@ -162,7 +162,11 @@ function logout() {
}) })
.then(() => { .then(() => {
userStore.logOut().then(() => { userStore.logOut().then(() => {
location.href = '/index' // location.href = '/workbench'
//退回到中台登录页
window.open(`${import.meta.env.VITE_APP_BASE_API1}/login?redirect=/workbench`)
// 然后立刻关闭自己
window.close()
}) })
}) })
.catch(() => {}) .catch(() => {})
......
...@@ -8,6 +8,8 @@ import 'element-plus/theme-chalk/dark/css-vars.css' ...@@ -8,6 +8,8 @@ import 'element-plus/theme-chalk/dark/css-vars.css'
import locale from 'element-plus/es/locale/lang/zh-cn' import locale from 'element-plus/es/locale/lang/zh-cn'
import '@/assets/styles/index.scss' // global css import '@/assets/styles/index.scss' // global css
// 引入 Iconfont 样式文件
import '@/assets/iconfont/iconfont.css'
import App from './App' import App from './App'
import store from './store' import store from './store'
...@@ -26,21 +28,28 @@ import elementIcons from '@/components/SvgIcon/svgicon' ...@@ -26,21 +28,28 @@ import elementIcons from '@/components/SvgIcon/svgicon'
import './permission' // permission control import './permission' // permission control
import { useDict } from '@/utils/dict' import { useDict } from '@/utils/dict'
import { getConfigKey } from "@/api/system/config" import { getConfigKey } from '@/api/system/config'
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi' import {
parseTime,
resetForm,
addDateRange,
handleTree,
selectDictLabel,
selectDictLabels
} from '@/utils/ruoyi'
import { getNowTime, formatToDate, formatToDateTime } from '@/utils/date'
// 分页组件 // 分页组件
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
// 自定义表格工具组件 // 自定义表格工具组件
import RightToolbar from '@/components/RightToolbar' import RightToolbar from '@/components/RightToolbar'
// 富文本组件 // 富文本组件
import Editor from "@/components/Editor" import Editor from '@/components/Editor'
// 文件上传组件 // 文件上传组件
import FileUpload from "@/components/FileUpload" import FileUpload from '@/components/FileUpload'
// 图片上传组件 // 图片上传组件
import ImageUpload from "@/components/ImageUpload" import ImageUpload from '@/components/ImageUpload'
// 图片预览组件 // 图片预览组件
import ImagePreview from "@/components/ImagePreview" import ImagePreview from '@/components/ImagePreview'
// 字典标签组件 // 字典标签组件
import DictTag from '@/components/DictTag' import DictTag from '@/components/DictTag'
...@@ -50,6 +59,9 @@ const app = createApp(App) ...@@ -50,6 +59,9 @@ const app = createApp(App)
app.config.globalProperties.useDict = useDict app.config.globalProperties.useDict = useDict
app.config.globalProperties.download = download app.config.globalProperties.download = download
app.config.globalProperties.parseTime = parseTime app.config.globalProperties.parseTime = parseTime
app.config.globalProperties.getNowTime = getNowTime
app.config.globalProperties.formatToDate = formatToDate
app.config.globalProperties.formatToDateTime = formatToDateTime
app.config.globalProperties.resetForm = resetForm app.config.globalProperties.resetForm = resetForm
app.config.globalProperties.handleTree = handleTree app.config.globalProperties.handleTree = handleTree
app.config.globalProperties.addDateRange = addDateRange app.config.globalProperties.addDateRange = addDateRange
......
...@@ -8,26 +8,36 @@ import useUserStore from '@/store/modules/user' // 使用命名导入 ...@@ -8,26 +8,36 @@ import useUserStore from '@/store/modules/user' // 使用命名导入
import useSettingsStore from '@/store/modules/settings' // 使用命名导入 import useSettingsStore from '@/store/modules/settings' // 使用命名导入
import usePermissionStore from '@/store/modules/permission' // 使用命名导入 import usePermissionStore from '@/store/modules/permission' // 使用命名导入
import { getToken, setToken, removeToken } from '@/utils/auth' import { getToken, setToken, removeToken } from '@/utils/auth'
import { de } from 'element-plus/es/locales.mjs'
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register'] const whiteList = ['/login', '/register', '/workbench']
const isWhiteList = path => { const isWhiteList = path => {
return whiteList.some(pattern => isPathMatch(pattern, path)) return whiteList.some(pattern => isPathMatch(pattern, path))
} }
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
console.log('跳转进来了', to)
// 带token是从外部跳转进系统的 // 带token是从外部跳转进系统的
if (to.query.token) { if (to.query.token) {
removeToken()
setToken(to.query.token) setToken(to.query.token)
useUserStore().setProjectInfo({ useUserStore().setProjectInfo({
tenantBizId: to.query.tenantBizId, tenantBizId: to.query.tenantBizId,
projectBizId: to.query.projectBizId projectBizId: to.query.projectBizId
}) })
localStorage.setItem(
'projectInfo',
JSON.stringify({
tenantBizId: to.query.tenantBizId,
projectBizId: to.query.projectBizId
})
)
}
if (localStorage.getItem('projectInfo')) {
useUserStore().setProjectInfo(JSON.parse(localStorage.getItem('projectInfo')))
} }
NProgress.start() NProgress.start()
if (getToken()) { if (getToken()) {
to.meta.title && useSettingsStore().setTitle(to.meta.title) to.meta.title && useSettingsStore().setTitle(to.meta.title)
...@@ -36,10 +46,38 @@ router.beforeEach((to, from, next) => { ...@@ -36,10 +46,38 @@ router.beforeEach((to, from, next) => {
next({ path: '/' }) next({ path: '/' })
NProgress.done() NProgress.done()
} else if (isWhiteList(to.path)) { } else if (isWhiteList(to.path)) {
next() if (useUserStore().roles.length === 0) {
} else { isRelogin.show = true
console.log('roles', useUserStore().roles.length)
// 判断当前用户是否已拉取完user_info信息
useUserStore()
.getInfo()
.then(() => {
isRelogin.show = false
usePermissionStore()
.generateRoutes()
.then(accessRoutes => {
// 根据roles权限生成可访问的路由表
accessRoutes.forEach(route => {
if (!isHttp(route.path)) {
router.addRoute(route) // 动态添加可访问路由表
}
})
next() // hack方法 确保addRoutes已完成
})
})
.catch(err => {
useUserStore()
.logOut()
.then(() => {
ElMessage.error(err)
next({ path: '/' })
})
})
} else {
next()
}
} else {
if (useUserStore().roles.length === 0) { if (useUserStore().roles.length === 0) {
isRelogin.show = true isRelogin.show = true
...@@ -57,6 +95,7 @@ router.beforeEach((to, from, next) => { ...@@ -57,6 +95,7 @@ router.beforeEach((to, from, next) => {
router.addRoute(route) // 动态添加可访问路由表 router.addRoute(route) // 动态添加可访问路由表
} }
}) })
// next({ path: '/' })
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
}) })
}) })
...@@ -75,10 +114,16 @@ router.beforeEach((to, from, next) => { ...@@ -75,10 +114,16 @@ router.beforeEach((to, from, next) => {
} else { } else {
// 没有token // 没有token
if (isWhiteList(to.path)) { if (isWhiteList(to.path)) {
// 在免登录白名单,直接进入 // 在免登录白名单,现在先跳回中台,后续根据具体需求在改动
next() // next()
window.open(`${import.meta.env.VITE_APP_BASE_API1}/login?redirect=/workbench`)
// 然后立刻关闭自己
window.close()
} else { } else {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 // next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
window.open(`${import.meta.env.VITE_APP_BASE_API1}/login?redirect=/workbench`)
// 然后立刻关闭自己
window.close()
NProgress.done() NProgress.done()
} }
} }
......
...@@ -21,19 +21,19 @@ export default { ...@@ -21,19 +21,19 @@ export default {
}, },
// 弹出提示 // 弹出提示
alert(content) { alert(content) {
ElMessageBox.alert(content, "系统提示") ElMessageBox.alert(content, '系统提示')
}, },
// 错误提示 // 错误提示
alertError(content) { alertError(content) {
ElMessageBox.alert(content, "系统提示", { type: 'error' }) ElMessageBox.alert(content, '系统提示', { type: 'error' })
}, },
// 成功提示 // 成功提示
alertSuccess(content) { alertSuccess(content) {
ElMessageBox.alert(content, "系统提示", { type: 'success' }) ElMessageBox.alert(content, '系统提示', { type: 'success' })
}, },
// 警告提示 // 警告提示
alertWarning(content) { alertWarning(content) {
ElMessageBox.alert(content, "系统提示", { type: 'warning' }) ElMessageBox.alert(content, '系统提示', { type: 'warning' })
}, },
// 通知提示 // 通知提示
notify(content) { notify(content) {
...@@ -52,19 +52,28 @@ export default { ...@@ -52,19 +52,28 @@ export default {
ElNotification.warning(content) ElNotification.warning(content)
}, },
// 确认窗体 // 确认窗体
confirm(content) { confirm(content, info) {
return ElMessageBox.confirm(content, "系统提示", { console.log('confirm', info)
confirmButtonText: '确定', let newInfo = {
cancelButtonText: '取消', confirmButtonText: info?.confirmButtonText || '确定',
type: "warning", cancelButtonText: info?.cancelButtonText || '取消',
showCancel: info?.showCancel == '0' ? false : true,
title: info?.title || '系统提示'
}
console.log('confirm', newInfo)
return ElMessageBox.confirm(content, newInfo.title, {
confirmButtonText: newInfo.confirmButtonText,
cancelButtonText: newInfo.cancelButtonText,
showCancelButton: newInfo.showCancel,
type: 'warning'
}) })
}, },
// 提交内容 // 提交内容
prompt(content) { prompt(content) {
return ElMessageBox.prompt(content, "系统提示", { return ElMessageBox.prompt(content, '系统提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: "warning", type: 'warning'
}) })
}, },
// 打开遮罩层 // 打开遮罩层
...@@ -72,7 +81,7 @@ export default { ...@@ -72,7 +81,7 @@ export default {
loadingInstance = ElLoading.service({ loadingInstance = ElLoading.service({
lock: true, lock: true,
text: content, text: content,
background: "rgba(0, 0, 0, 0.7)", background: 'rgba(0, 0, 0, 0.7)'
}) })
}, },
// 关闭遮罩层 // 关闭遮罩层
......
...@@ -48,7 +48,7 @@ export const constantRoutes = [ ...@@ -48,7 +48,7 @@ export const constantRoutes = [
hidden: true hidden: true
}, },
{ {
path: "/:pathMatch(.*)*", path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404'), component: () => import('@/views/error/404'),
hidden: true hidden: true
}, },
...@@ -70,7 +70,7 @@ export const constantRoutes = [ ...@@ -70,7 +70,7 @@ export const constantRoutes = [
meta: { title: '工作台', icon: 'dashboard', affix: true } meta: { title: '工作台', icon: 'dashboard', affix: true }
} }
] ]
}, }
// { // {
// path: '', // path: '',
// component: Layout, // component: Layout,
...@@ -198,7 +198,7 @@ const router = createRouter({ ...@@ -198,7 +198,7 @@ const router = createRouter({
return savedPosition return savedPosition
} }
return { top: 0 } return { top: 0 }
}, }
}) })
// 重置路由函数 // 重置路由函数
......
import auth from '@/plugins/auth' import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router' import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu' import { getRouters } from '@/api/menu'
import { getMenuLists } from '@/api/common'
import Layout from '@/layout/index' import Layout from '@/layout/index'
import ParentView from '@/components/ParentView' import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink' import InnerLink from '@/layout/components/InnerLink'
...@@ -21,9 +22,6 @@ const usePermissionStore = defineStore('permission', { ...@@ -21,9 +22,6 @@ const usePermissionStore = defineStore('permission', {
setRoutes(routes) { setRoutes(routes) {
this.addRoutes = routes this.addRoutes = routes
this.routes = constantRoutes.concat(routes) this.routes = constantRoutes.concat(routes)
console.log('====================================')
console.log('this.routes', this.routes)
console.log('====================================')
}, },
setDefaultRoutes(routes) { setDefaultRoutes(routes) {
this.defaultRoutes = constantRoutes.concat(routes) this.defaultRoutes = constantRoutes.concat(routes)
...@@ -37,25 +35,27 @@ const usePermissionStore = defineStore('permission', { ...@@ -37,25 +35,27 @@ const usePermissionStore = defineStore('permission', {
generateRoutes() { generateRoutes() {
return new Promise(resolve => { return new Promise(resolve => {
const userStore = useUserStore() const userStore = useUserStore()
const tenantBizId = getMenuLists(userStore.projectInfo.projectBizId, userStore.projectInfo.tenantBizId).then(
userStore.currentTenant.apiLoginTenantInfoResponse.tenantBizId || 'tenant_1001' res => {
const sdata = JSON.parse(JSON.stringify(res.data))
getRouters(tenantBizId).then(res => { const rdata = JSON.parse(JSON.stringify(res.data))
const sdata = JSON.parse(JSON.stringify(res.data)) const defaultData = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const defaultData = JSON.parse(JSON.stringify(res.data)) const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const sidebarRoutes = filterAsyncRouter(sdata) const defaultRoutes = filterAsyncRouter(defaultData)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const defaultRoutes = filterAsyncRouter(defaultData) this.setRoutes(rewriteRoutes)
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
this.setRoutes(rewriteRoutes) this.setDefaultRoutes(sidebarRoutes)
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes)) this.setTopbarRoutes(defaultRoutes)
this.setDefaultRoutes(sidebarRoutes) console.log('====================================')
this.setTopbarRoutes(defaultRoutes) console.log('rewriteRoutes', rewriteRoutes)
console.log('====================================')
resolve(rewriteRoutes)
}) resolve(rewriteRoutes)
}
)
}) })
} }
} }
...@@ -86,7 +86,7 @@ function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { ...@@ -86,7 +86,7 @@ function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
route.meta.title = route.menuName route.meta.title = route.menuName
route.meta.icon = route.icon route.meta.icon = route.icon
route.meta.isCache = route.isCache === 1 route.meta.isCache = route.isCache === 1
route.name = route.routeName
//确保单子菜单的父菜单也显示 //确保单子菜单的父菜单也显示
if (route.menuType === 1) { if (route.menuType === 1) {
route.alwaysShow = true // 强制显示父菜单 route.alwaysShow = true // 强制显示父菜单
......
import router from '@/router' import router from '@/router'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { login, logout, getInfo } from '@/api/login' import { login, logout, getInfo } from '@/api/login'
import { getUserInfo } from '@/api/common'
import { getToken, setToken, removeToken } from '@/utils/auth' import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from '@/utils/validate' import { isHttp, isEmpty } from '@/utils/validate'
import defAva from '@/assets/images/profile.jpg' import defAva from '@/assets/images/profile.jpg'
...@@ -18,7 +19,8 @@ const useUserStore = defineStore('user', { ...@@ -18,7 +19,8 @@ const useUserStore = defineStore('user', {
permissions: [], permissions: [],
tenants: [], // 新增租户列表 tenants: [], // 新增租户列表
currentTenant: null, // 新增当前租户 currentTenant: null, // 新增当前租户
projectInfo:null //保存从中台跳转过来的项目信息 projectInfo: null, //保存从中台跳转过来的项目信息
userDetail: null //保存从中台跳转过来的用户信息
}), }),
actions: { actions: {
// 登录 // 登录
...@@ -75,7 +77,7 @@ const useUserStore = defineStore('user', { ...@@ -75,7 +77,7 @@ const useUserStore = defineStore('user', {
this.nickName = user.nickName this.nickName = user.nickName
this.avatar = avatar this.avatar = avatar
this.isSuperAdmin = user.isSuperAdmin this.isSuperAdmin = user.isSuperAdmin
this.userDetail = user //新增的中台用户信息
/* 初始密码提示 */ /* 初始密码提示 */
if (res.data.isDefaultModifyPwd) { if (res.data.isDefaultModifyPwd) {
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', {
......
{
"id": 12,
"customerBizId": "customer_XQH9OmZsSNafYMhc",
"customCode": null,
"lastName": "11",
"firstName": "11",
"name": "11",
"lastNamePinyin": "11",
"firstNamePinyin": "11",
"pinyin": "11",
"title": "Dr",
"gender": "2",
"birthdate": null,
"abnormal": null,
"age": "12",
"areaCode": "+86",
"phone": "11111",
"email": "11111",
"smoke": "1",
"smokeQuantity": "10",
"companyType": "11111",
"source": null,
"idType": "passport",
"idCard": "555",
"passport": null,
"eepCode": null,
"marriage": "MARRIED",
"birthplace": "11111111",
"education": "UNIVERSITY",
"customerExpandBizId": "customer_expand_l3eOcWeqGd1IFDJg",
"country": "CHINESE",
"countryName": "中国",
"createTime": "2025-09-16T11:40:58.000+08:00",
"customerType": null,
"residenceAreaCode": "+44",
"residenceTelephone": "22222",
"longtimeAbroad": "1",
"addressList": [
{
"type": "residenceAddress",
"region": "111",
"city": "222",
"street": "333",
"location": "444"
},
{
"type": "residentialAddress",
"region": "111",
"city": "222",
"street": "333",
"location": "444"
},
{
"type": "mailingAddress",
"region": "333",
"city": "333",
"street": "444",
"location": "555"
},
{
"type": "companyAddress",
"region": "666",
"city": "777",
"street": "888",
"location": "999"
}
],
"companyName": "444",
"companyAreaCode": "+65",
"companyTelephone": "5555",
"position": "555",
"workYear": "55",
"salary": 55555
}
// 深拷贝函数,避免对象引用问题
export default function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj.getTime())
if (obj instanceof Array) return obj.map(item => deepClone(item))
if (typeof obj === 'object') {
const clonedObj = {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key])
}
}
return clonedObj
}
}
import dayjs from 'dayjs'
export function formatIsoToDateTime(isoStr) { export function formatIsoToDateTime(isoStr) {
// 处理 null/undefined 情况,避免报错 // 处理 null/undefined 情况,避免报错
if (!isoStr) return undefined; if (!isoStr) return undefined
// 替换 T 为空格,返回 "YYYY-MM-DD HH:mm:ss" // 替换 T 为空格,返回 "YYYY-MM-DD HH:mm:ss"
return isoStr.replace('T', ' '); return isoStr.replace('T', ' ')
}
console.log(dayjs)
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
const DATE_FORMAT = 'YYYY-MM-DD'
export function formatToDateTime(date, format = DATE_TIME_FORMAT) {
return dayjs(date).format(format)
}
export function formatToDate(date, format = DATE_FORMAT) {
if (!date) return ''
return dayjs(date).format(format)
}
export const getNowTime = (format = DATE_TIME_FORMAT) => {
return dayjs().format(format)
} }
import useDictStore from '@/store/modules/dict' import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data' import { getDicts } from '@/api/system/dict/data'
/** /**
* 获取字典数据 * 获取字典数据
*/ */
...@@ -24,7 +23,6 @@ export function useDict(...args) { ...@@ -24,7 +23,6 @@ export function useDict(...args) {
})() })()
} }
// /** // /**
// * 获取字典数据 // * 获取字典数据
// */ // */
......
...@@ -40,7 +40,9 @@ function buildFormTemplate(conf, child, type) { ...@@ -40,7 +40,9 @@ function buildFormTemplate(conf, child, type) {
labelPosition = `label-position="${conf.labelPosition}"` labelPosition = `label-position="${conf.labelPosition}"`
} }
const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}> let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${
conf.formRules
}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}>
${child} ${child}
${buildFromBtns(conf, type)} ${buildFromBtns(conf, type)}
</el-form>` </el-form>`
...@@ -108,9 +110,7 @@ const layouts = { ...@@ -108,9 +110,7 @@ const layouts = {
const tags = { const tags = {
'el-button': el => { 'el-button': el => {
const { const { tag, disabled } = attrBuilder(el)
tag, disabled
} = attrBuilder(el)
const type = el.type ? `type="${el.type}"` : '' const type = el.type ? `type="${el.type}"` : ''
const icon = el.icon ? `icon="${el.icon}"` : '' const icon = el.icon ? `icon="${el.icon}"` : ''
const size = el.size ? `size="${el.size}"` : '' const size = el.size ? `size="${el.size}"` : ''
...@@ -120,9 +120,7 @@ const tags = { ...@@ -120,9 +120,7 @@ const tags = {
return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}</${el.tag}>` return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}</${el.tag}>`
}, },
'el-input': el => { 'el-input': el => {
const { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el)
disabled, vModel, clearable, placeholder, width
} = attrBuilder(el)
const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
const readonly = el.readonly ? 'readonly' : '' const readonly = el.readonly ? 'readonly' : ''
...@@ -130,9 +128,10 @@ const tags = { ...@@ -130,9 +128,10 @@ const tags = {
const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
const showPassword = el['show-password'] ? 'show-password' : '' const showPassword = el['show-password'] ? 'show-password' : ''
const type = el.type ? `type="${el.type}"` : '' const type = el.type ? `type="${el.type}"` : ''
const autosize = el.autosize && el.autosize.minRows const autosize =
? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` el.autosize && el.autosize.minRows
: '' ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
: ''
let child = buildElInputChild(el) let child = buildElInputChild(el)
if (child) child = `\n${child}\n` // 换行 if (child) child = `\n${child}\n` // 换行
...@@ -140,7 +139,9 @@ const tags = { ...@@ -140,7 +139,9 @@ const tags = {
}, },
'el-input-number': el => { 'el-input-number': el => {
const { disabled, vModel, placeholder } = attrBuilder(el) const { disabled, vModel, placeholder } = attrBuilder(el)
const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' const controlsPosition = el['controls-position']
? `controls-position=${el['controls-position']}`
: ''
const min = el.min ? `:min='${el.min}'` : '' const min = el.min ? `:min='${el.min}'` : ''
const max = el.max ? `:max='${el.max}'` : '' const max = el.max ? `:max='${el.max}'` : ''
const step = el.step ? `:step='${el.step}'` : '' const step = el.step ? `:step='${el.step}'` : ''
...@@ -150,9 +151,7 @@ const tags = { ...@@ -150,9 +151,7 @@ const tags = {
return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>` return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`
}, },
'el-select': el => { 'el-select': el => {
const { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el)
disabled, vModel, clearable, placeholder, width
} = attrBuilder(el)
const filterable = el.filterable ? 'filterable' : '' const filterable = el.filterable ? 'filterable' : ''
const multiple = el.multiple ? 'multiple' : '' const multiple = el.multiple ? 'multiple' : ''
let child = buildElSelectChild(el) let child = buildElSelectChild(el)
...@@ -184,15 +183,17 @@ const tags = { ...@@ -184,15 +183,17 @@ const tags = {
const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' const activeValue =
const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
const inactiveValue =
el['inactive-value'] !== false
? `:inactive-value='${JSON.stringify(el['inactive-value'])}'`
: ''
return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>` return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`
}, },
'el-cascader': el => { 'el-cascader': el => {
const { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el)
disabled, vModel, clearable, placeholder, width
} = attrBuilder(el)
const options = el.options ? `:options="${el.vModel}Options"` : '' const options = el.options ? `:options="${el.vModel}Options"` : ''
const props = el.props ? `:props="${el.vModel}Props"` : '' const props = el.props ? `:props="${el.vModel}Props"` : ''
const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
...@@ -212,24 +213,26 @@ const tags = { ...@@ -212,24 +213,26 @@ const tags = {
return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>` return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`
}, },
'el-time-picker': el => { 'el-time-picker': el => {
const { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el)
disabled, vModel, clearable, placeholder, width const startPlaceholder = el['start-placeholder']
} = attrBuilder(el) ? `start-placeholder="${el['start-placeholder']}"`
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' : ''
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
const isRange = el['is-range'] ? 'is-range' : '' const isRange = el['is-range'] ? 'is-range' : ''
const format = el.format ? `format="${el.format}"` : '' const format = el.format ? `format="${el.format}"` : ''
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' const pickerOptions = el['picker-options']
? `:picker-options='${JSON.stringify(el['picker-options'])}'`
: ''
return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>` return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`
}, },
'el-date-picker': el => { 'el-date-picker': el => {
const { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el)
disabled, vModel, clearable, placeholder, width const startPlaceholder = el['start-placeholder']
} = attrBuilder(el) ? `start-placeholder="${el['start-placeholder']}"`
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' : ''
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
const format = el.format ? `format="${el.format}"` : '' const format = el.format ? `format="${el.format}"` : ''
...@@ -257,7 +260,7 @@ const tags = { ...@@ -257,7 +260,7 @@ const tags = {
return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>` return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`
}, },
'el-upload': el => { 'el-upload': el => {
const disabled = el.disabled ? ':disabled=\'true\'' : '' const disabled = el.disabled ? ":disabled='true'" : ''
const action = el.action ? `:action="${el.vModel}Action"` : '' const action = el.action ? `:action="${el.vModel}Action"` : ''
const multiple = el.multiple ? 'multiple' : '' const multiple = el.multiple ? 'multiple' : ''
const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
...@@ -280,7 +283,7 @@ function attrBuilder(el) { ...@@ -280,7 +283,7 @@ function attrBuilder(el) {
clearable: el.clearable ? 'clearable' : '', clearable: el.clearable ? 'clearable' : '',
placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
disabled: el.disabled ? ':disabled=\'true\'' : '' disabled: el.disabled ? ":disabled='true'" : ''
} }
} }
...@@ -308,7 +311,9 @@ function buildElInputChild(conf) { ...@@ -308,7 +311,9 @@ function buildElInputChild(conf) {
function buildElSelectChild(conf) { function buildElSelectChild(conf) {
const children = [] const children = []
if (conf.options && conf.options.length) { if (conf.options && conf.options.length) {
children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`) children.push(
`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`
)
} }
return children.join('\n') return children.join('\n')
} }
...@@ -318,7 +323,9 @@ function buildElRadioGroupChild(conf) { ...@@ -318,7 +323,9 @@ function buildElRadioGroupChild(conf) {
if (conf.options && conf.options.length) { if (conf.options && conf.options.length) {
const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
const border = conf.border ? 'border' : '' const border = conf.border ? 'border' : ''
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :value="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`) children.push(
`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :value="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`
)
} }
return children.join('\n') return children.join('\n')
} }
...@@ -328,7 +335,9 @@ function buildElCheckboxGroupChild(conf) { ...@@ -328,7 +335,9 @@ function buildElCheckboxGroupChild(conf) {
if (conf.options && conf.options.length) { if (conf.options && conf.options.length) {
const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
const border = conf.border ? 'border' : '' const border = conf.border ? 'border' : ''
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :value="item.label" :disabled="item.disabled" ${border} />`) children.push(
`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :value="item.label" :disabled="item.disabled" ${border} />`
)
} }
return children.join('\n') return children.join('\n')
} }
...@@ -336,8 +345,14 @@ function buildElCheckboxGroupChild(conf) { ...@@ -336,8 +345,14 @@ function buildElCheckboxGroupChild(conf) {
function buildElUploadChild(conf) { function buildElUploadChild(conf) {
const list = [] const list = []
if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>') if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`) else
if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit}${conf.accept}文件</div>`) list.push(
`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`
)
if (conf.showTip)
list.push(
`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit}${conf.accept}文件</div>`
)
return list.join('\n') return list.join('\n')
} }
......
import axios from 'axios' import axios from 'axios'
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus' import { ElNotification, ElMessageBox, ElMessage, ElLoading } from 'element-plus'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode' import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from '@/utils/ruoyi' import { tansParams, blobValidate } from '@/utils/ruoyi'
...@@ -21,89 +21,106 @@ const service = axios.create({ ...@@ -21,89 +21,106 @@ const service = axios.create({
}) })
// request拦截器 // request拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(
config => {
// 添加租户ID到请求头
const userStore = useUserStore()
// if (userStore.currentTenant) {
// const tenantBizId = userStore.currentTenant.apiLoginTenantInfoResponse.tenantBizId
// config.headers['X-Tenant-ID'] = tenantBizId
// }
// 添加租户ID到请求头 // 是否需要设置 token
const userStore = useUserStore() const isToken = (config.headers || {}).isToken === false
if (userStore.currentTenant) { // 是否需要防止数据重复提交
const tenantBizId = userStore.currentTenant.apiLoginTenantInfoResponse.tenantBizId const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
config.headers['X-Tenant-ID'] = tenantBizId if (getToken() && !isToken) {
} config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params)
url = url.slice(0, -1)
config.params = {}
config.url = url
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
} }
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小 // get请求映射params参数
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M if (config.method === 'get' && config.params) {
if (requestSize >= limitSize) { let url = config.url + '?' + tansParams(config.params)
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。') url = url.slice(0, -1)
return config config.params = {}
config.url = url
} }
const sessionObj = cache.session.getJSON('sessionObj') if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
if (sessionObj === undefined || sessionObj === null || sessionObj === '') { const requestObj = {
cache.session.setJSON('sessionObj', requestObj) url: config.url,
} else { data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
const s_url = sessionObj.url // 请求地址 time: new Date().getTime()
const s_data = sessionObj.data // 请求数据 }
const s_time = sessionObj.time // 请求时间 const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交 const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { if (requestSize >= limitSize) {
const message = '数据正在处理,请勿重复提交' console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')
console.warn(`[${s_url}]: ` + message) return config
return Promise.reject(new Error(message)) }
} else { const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj) cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url // 请求地址
const s_data = sessionObj.data // 请求数据
const s_time = sessionObj.time // 请求时间
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
// if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
// const message = '数据正在处理,请勿重复提交'
// console.warn(`[${s_url}]: ` + message)
// return Promise.reject(new Error(message))
// } else {
// cache.session.setJSON('sessionObj', requestObj)
// }
} }
} }
} return config
return config },
}, error => { error => {
console.log(error) console.log(error)
Promise.reject(error) Promise.reject(error)
}) }
)
// 响应拦截器 // 响应拦截器
service.interceptors.response.use(res => { service.interceptors.response.use(
res => {
// 未设置状态码则默认成功状态 // 未设置状态码则默认成功状态
const code = res.data.code || 200 const code = res.data.code || 200
// 获取错误信息 // 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default'] const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回 // 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data return res.data
} }
if (code === 401) { if (code === 401) {
if (!isRelogin.show) { if (!isRelogin.show) {
isRelogin.show = true isRelogin.show = true
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
isRelogin.show = false confirmButtonText: '重新登录',
useUserStore().logOut().then(() => { cancelButtonText: '取消',
location.href = '/index' type: 'warning'
})
.then(() => {
isRelogin.show = false
useUserStore()
.logOut()
.then(() => {
// location.href = '/index'
//token过期,跳转回中台的登录页
window.open(`${import.meta.env.VITE_APP_BASE_API1}/login?redirect=/workbench`)
// 然后立刻关闭自己
window.close()
})
}) })
}).catch(() => { .catch(() => {
isRelogin.show = false isRelogin.show = false
}) })
} }
return Promise.reject('无效的会话,或者会话已过期,请重新登录。') return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) { } else if (code === 500) {
ElMessage({ message: msg, type: 'error' }) ElMessage({ message: msg, type: 'error' })
console.log('msg', Promise.reject(new Error(msg)))
return Promise.reject(new Error(msg)) return Promise.reject(new Error(msg))
} else if (code === 601) { } else if (code === 601) {
ElMessage({ message: msg, type: 'warning' }) ElMessage({ message: msg, type: 'warning' })
...@@ -112,18 +129,18 @@ service.interceptors.response.use(res => { ...@@ -112,18 +129,18 @@ service.interceptors.response.use(res => {
ElNotification.error({ title: msg }) ElNotification.error({ title: msg })
return Promise.reject('error') return Promise.reject('error')
} else { } else {
return Promise.resolve(res.data) return Promise.resolve(res.data)
} }
}, },
error => { error => {
console.log('err' + error) console.log('err' + error)
let { message } = error let { message } = error
if (message == "Network Error") { if (message == 'Network Error') {
message = "后端接口连接异常" message = '后端接口连接异常'
} else if (message.includes("timeout")) { } else if (message.includes('timeout')) {
message = "系统接口请求超时" message = '系统接口请求超时'
} else if (message.includes("Request failed with status code")) { } else if (message.includes('Request failed with status code')) {
message = "系统接口" + message.substr(message.length - 3) + "异常" message = '系统接口' + message.substr(message.length - 3) + '异常'
} }
ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error) return Promise.reject(error)
...@@ -132,29 +149,39 @@ service.interceptors.response.use(res => { ...@@ -132,29 +149,39 @@ service.interceptors.response.use(res => {
// 通用下载方法 // 通用下载方法
export function download(url, params, filename, config) { export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }) downloadLoadingInstance = ElLoading.service({
return service.post(url, params, { text: '正在下载数据,请稍候',
transformRequest: [(params) => { return tansParams(params) }], background: 'rgba(0, 0, 0, 0.7)'
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config
}).then(async (data) => {
const isBlob = blobValidate(data)
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text()
const rspObj = JSON.parse(resText)
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
ElMessage.error(errMsg)
}
downloadLoadingInstance.close()
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
}) })
return service
.post(url, params, {
transformRequest: [
params => {
return tansParams(params)
}
],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config
})
.then(async data => {
const isBlob = blobValidate(data)
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text()
const rspObj = JSON.parse(resText)
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
ElMessage.error(errMsg)
}
downloadLoadingInstance.close()
})
.catch(r => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
} }
export default service export default service
<template>
<div class="drawerBox">
<!-- direction="btt" -->
<el-drawer v-model="drawer" :before-close="handleBeforeClose" size="50%">
<template #header>
<h2>{{ drawerInfo.name }}</h2>
</template>
<template #default>
<div class="drawerContent">
<div class="item" v-for="e in menuList" :key="e.name">
<div class="left">{{ e.name }}</div>
<div class="right">
<el-input class="addressInput" v-model="e.value" :placeholder="e.placeholder" />
<el-icon v-if="e.icon" class="rightArrow"> <ArrowRight /></el-icon>
</div>
</div>
<div class="quickBox" v-if="addressList.length > 0">
<span class="title">快捷地址</span>
<div
class="quickItem"
v-for="(quick, index) in addressList"
:key="index"
@click="setAddress(quick)"
>
<el-icon class="locationIcon"><Location /></el-icon>
<span>{{ quick.region }}{{ quick.city }}{{ quick.street }}{{ quick.location }}</span>
</div>
</div>
</div>
</template>
<template #footer>
<div class="buttonBox">
<div class="left" @click.stop="resetClick">
<el-icon class="buttonIcon"><RefreshRight /></el-icon>
<span>重置</span>
</div>
<div class="right" @click.stop="confirmClick">
<el-icon><Check /></el-icon>
<span>保存</span>
</div>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup name="AddressDrawer">
import deepClone from '@/utils/common'
import { watch, ref } from 'vue'
const props = defineProps({
showAddressDrawer: {
type: Boolean,
default: null
},
drawerInfo: {
type: Object,
default: () => {
return {}
}
},
addressMenuList: {
type: Array,
default: () => {
return []
}
},
addressQuickList: {
type: Array,
default: () => {
return []
}
}
})
const emit = defineEmits(['close', 'confirmDrawer'])
const { proxy } = getCurrentInstance()
const drawer = ref(false)
const menuList = ref([])
const addressList = ref([
{
region: '中国',
city: '北京',
street: '朝阳区',
location: '三里屯'
}
])
// 处理抽屉关闭前的事件
const handleBeforeClose = done => {
menuList.value = props.addressMenuList
emit('close')
drawer.value = false
done()
}
const resetClick = () => {
menuList.value.forEach(e => {
e.value = ''
})
}
const confirmClick = () => {
let info = JSON.parse(JSON.stringify(props.drawerInfo))
//收集填写的地址传给父组件
let location = {}
menuList.value.forEach(e => {
location[e.key] = e.value
})
for (const key in props.drawerInfo) {
if (key === info.key) {
info[key] = location
}
}
emit('confirmDrawer', info)
drawer.value = false
}
//通过快捷电话设置区号和电话号码
const setAddress = item => {
menuList.value.forEach(e => {
for (const key in item) {
if (e.key === key) {
e.value = item[e.key]
}
}
})
}
// 初始化menuList数据
const initMenuList = () => {
// 使用深拷贝创建新的数组,避免修改props
menuList.value = deepClone(props.addressMenuList)
}
// 监听父组件传递的showAddressDrawer变化
watch(
() => props.showAddressDrawer,
val => {
drawer.value = val
if (val) {
// 每次打开抽屉时初始化数据
nextTick(() => {
initMenuList()
})
}
}
)
watch(
() => props.addressQuickList,
val => {
addressList.value = val
},
{ immediate: true }
)
watch(drawer, val => {
if (!val) {
proxy.$emit('close', false)
}
})
</script>
<style lang="scss" scoped>
::v-deep .el-drawer__header {
margin-bottom: 0 !important;
}
::v-deep .el-drawer__footer {
padding: 0;
}
.addressInput ::v-deep .el-input__wrapper {
box-shadow: none !important;
}
.addressInput ::v-deep .el-input__inner::placeholder {
text-align: right;
}
.addressInput ::v-deep .el-input__inner {
text-align: right;
}
.addressInput ::v-deep .el-input__wrapper {
padding: 0 !important;
}
.drawerContent {
.item {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #e5e6e8;
margin-bottom: 20px;
padding: 10px;
color: #4e5969;
font-size: 14px;
.right {
display: flex;
align-items: center;
}
.rightArrow {
color: #4e5969;
font-size: 14px;
}
}
.quickBox {
.title {
font-size: 18px;
border-left: 4px solid #165dff;
padding-left: 5px;
}
.quickItem {
display: flex;
align-items: center;
justify-content: flex-start;
margin-top: 10px;
color: #1d2129;
font-size: 14px;
border: 1px solid #e5e6e8;
border-radius: 3px;
padding: 10px;
.locationIcon {
color: #1d2129;
font-size: 16px;
margin-right: 5px;
}
}
}
}
.buttonBox {
box-shadow: 0 -1px 14px #00000014;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.left,
.right {
display: flex;
align-items: center;
justify-content: center;
width: 50%;
font-size: 16px;
height: 60px;
}
.left {
background-color: #fff;
color: #165dff;
.buttonIcon {
font-size: 16px;
color: #165dff;
}
}
.right {
background-color: #165dff;
color: #fff;
.buttonIcon {
font-size: 16px;
color: #fff;
}
}
}
</style>
<template>
<div class="countryDrawerBox">
<!-- direction="btt" -->
<el-drawer
v-model="countryDrawer"
:before-close="handleBeforeClose"
class="countryBox"
size="50%"
>
<template #header>
<h2>国家/地区</h2>
</template>
<template #default>
<div class="countryContent">
<el-row>
<!-- 左侧 -->
<el-col :sm="20" :lg="21" class="content-col">
<el-row>
<el-col :span="24">
<div class="searchBox">
<el-input
class="searchInput"
v-model="searchValue"
placeholder="请输入国家/地区名称"
@change="searchCountry"
/>
<el-icon class="searchIcon"><Search /></el-icon>
</div>
</el-col>
</el-row>
<div class="hotCountryBox" ref="containerRef">
<div v-if="!searchValue">
<div class="title">热门国家/地区</div>
<div class="hotCountryList">
<el-row :gutter="20">
<template v-for="(item, index1) in hotCountriesList" :key="item.name">
<el-col :sm="12" :lg="8" @click="handleCountryClick(item)">
<div class="hotCountryItem">
<span>{{ item.name }}</span>
<span>{{ item.areaCode }}</span>
</div>
</el-col>
</template>
</el-row>
</div>
<div class="hotCountryList">
<template v-for="item in countryList" :key="item.areaCode">
<el-row>
<el-col :sm="24" :lg="24" style="margin: 5px 0">{{ item.title }}</el-col>
</el-row>
<el-row :gutter="20" :id="`${item.title}`">
<template v-for="(data, index2) in item.data" :key="item.name">
<el-col :span="24" @click="handleCountryClick(data)">
<div class="hotCountryItem">
<span>{{ data.name }}</span>
<span>{{ data.areaCode }}</span>
</div>
</el-col>
</template>
</el-row>
</template>
</div>
</div>
<div v-if="searchValue">
<div class="hotCountryList">
<el-row :gutter="20">
<template v-for="item in searchList" :key="item.name">
<el-col :sm="24" :lg="24" @click="handleCountryClick(item)">
<div class="hotCountryItem">
<span>{{ item.name }}</span>
<span>{{ item.areaCode }}</span>
</div>
</el-col>
</template>
</el-row>
</div>
</div>
</div>
</el-col>
<!-- 右侧锚点 -->
<el-col :sm="4" :lg="3">
<div class="anchor-wrapper">
<el-anchor
:container="containerRef"
direction="vertical"
type="default"
:offset="30"
@click="handleAnchorClick"
>
<el-anchor-link
v-for="item in anchorList"
:href="'#' + item.title"
:title="item.title"
/>
</el-anchor>
</div>
</el-col>
</el-row>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup>
import { watch, ref } from 'vue'
import { getCountryInfo, getSearchCountry } from '@/api/common'
const props = defineProps({
showCountryDrawer: {
type: Boolean,
default: null
},
drawerInfo: {
type: Object,
default: () => {
return {}
}
}
})
const emit = defineEmits(['close', 'confirmCountry'])
const countryDrawer = ref(false)
const countryList = ref([])
const originalData = ref([])
const hotCountriesList = ref([])
const containerRef = ref(null)
const anchorList = ref([])
const searchValue = ref('')
const searchList = ref([])
const isSearchLoading = ref(false)
const searchCountry = () => {
isSearchLoading.value = true
if (searchValue.value.trim()) {
getSearchCountry(searchValue.value.trim()).then(res => {
if (res.code === 200) {
res.data.countryList.forEach(d => {
d.areaCode = `+${d.areaCode}`
})
searchList.value = res.data.countryList
isSearchLoading.value = false
}
})
}
}
const handleCountryClick = item => {
emit('confirmCountry', item)
countryDrawer.value = false
resetSearch()
}
// 锚点事件
const handleAnchorClick = e => {
e.preventDefault()
}
// 处理抽屉关闭前的事件
const handleBeforeClose = done => {
emit('close')
countryDrawer.value = false
resetSearch()
done()
}
// 生成A-Z的分组结构
const generateGroups = () => {
const groups = []
// 生成26个字母的分组
for (let i = 0; i < 26; i++) {
const letter = String.fromCharCode(65 + i)
groups.push({
title: letter,
data: []
})
}
// 将国家数据分配到对应分组
originalData.value.forEach(country => {
const firstLetter = country.group.toUpperCase()
const group = groups.find(g => g.title === firstLetter)
if (group) {
group.data.push(country)
}
})
// 按国家名称排序每个分组内的数据
groups.forEach(group => {
group.data.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'))
})
return groups.filter(group => group.data.length > 0)
}
// 获取国家信息
const getCountry = () => {
getCountryInfo().then(res => {
if (res.code === 200) {
originalData.value = res.data.groupCountries
const result = generateGroups()
result.forEach(item => {
anchorList.value.push({ title: item.title })
item.data.forEach(d => {
d.areaCode = `+${d.areaCode}`
})
})
res.data.hotCountries.forEach(item => {
item.areaCode = `+${item.areaCode}`
})
hotCountriesList.value = res.data.hotCountries
countryList.value = result
}
})
}
const resetSearch = () => {
searchValue.value = ''
searchList.value = []
isSearchLoading.value = false
}
watch(searchValue, val => {
if (!val) {
searchList.value = []
isSearchLoading.value = false
}
})
watch(
() => props.showCountryDrawer,
val => {
countryDrawer.value = val
if (countryDrawer.value) {
getCountry()
}
}
)
watch(countryDrawer, val => {
if (!val) {
resetSearch()
emit('close', false)
}
})
</script>
<style lang="scss" scoped>
::v-deep .el-drawer__header {
margin-bottom: 0 !important;
}
.searchInput ::v-deep .el-input__wrapper {
box-shadow: none !important;
background-color: #fafbfd !important;
}
.searchInput ::v-deep .el-input__inner::placeholder {
text-align: left !important;
}
.searchInput ::v-deep .el-input__inner {
text-align: left !important;
}
.countryContent {
box-sizing: border-box;
.searchBox {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
background-color: #fafbfd;
width: 100%;
border-radius: 10px;
padding: 4px 10px;
.el-input {
width: 80%;
}
.searchIcon {
font-size: 20px;
color: #1d2129;
}
}
.hotCountryBox {
overflow: hidden;
height: calc(100vh - 100px);
overflow-y: auto;
box-sizing: border-box;
width: 100%;
scrollbar-width: none;
-ms-overflow-style: none;
.title {
box-sizing: border-box;
font-size: 18px;
border-left: 4px solid #165dff;
padding-left: 5px;
margin-bottom: 10px;
}
.hotCountryList {
box-sizing: border-box;
width: 100%;
.hotCountryItem {
box-sizing: border-box;
height: 40px;
padding: 5px;
border: 1px solid #e5e6e8;
border-radius: 7px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 13px;
color: #595959;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 0;
}
.loading-text {
margin-top: 10px;
color: #909399;
}
.empty-state {
text-align: center;
padding: 40px 0;
color: #909399;
}
}
}
.hotCountryBox::-webkit-scrollbar {
display: none;
}
.anchor-wrapper {
box-sizing: border-box;
margin-left: 20px;
border-left: 2px solid #d8ebff;
::v-deep(.el-anchor) {
.el-anchor__link {
padding: 4px 0;
.el-anchor__link-title {
font-size: 16px;
color: #1d2129;
&.is-active {
color: #165dff;
font-weight: 500;
}
}
}
}
::v-deep(.el-anchor__marker) {
height: 10px !important;
left: -6px !important;
border-radius: 50% !important;
width: 10px !important;
border: 1px solid #409eff !important;
background-color: transparent !important;
}
}
}
</style>
<template>
<div class="stepBox">
<div class="stepItem" v-for="step in stepList" :key="step.id">
<div
class="circle"
:class="{
finfish: step.finish,
todo: step.todo,
unFinish: step.unFinish
}"
>
<el-icon v-if="step.finish"><Check /></el-icon>
<span v-else>{{ step.id }}</span>
</div>
<div class="stepTitle">{{ step.title }}</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
stepList: {
type: Array,
default: () => []
}
})
</script>
<style lang="scss" scoped>
.stepBox {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
.stepItem {
display: flex;
justify-content: center;
align-items: center;
margin-right: 100px;
box-sizing: border-box;
.circle {
width: 35px;
height: 35px;
box-sizing: border-box;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
.el-icon {
font-size: 16px;
font-weight: 600;
}
}
.finfish {
background-color: #e8f3ff;
color: #165dff;
}
.todo {
background-color: #165dff;
color: #fff;
}
.unFinish {
background-color: #f2f3f5;
color: #4e5969;
}
.stepTitle {
font-size: 14px;
margin-left: 10px;
}
}
.stepItem:last-child {
margin-right: 0;
}
}
</style>
<template>
<div class="drawerBox">
<!-- direction="btt" -->
<el-drawer v-model="drawer" :before-close="handleBeforeClose" size="50%">
<template #header>
<h2>{{ drawerInfo.label }}</h2>
</template>
<template #default>
<div class="drawerContent">
<div class="item" v-for="e in menuList" :key="e.name">
<div class="left">{{ e.name }}</div>
<div class="right" @click="openCountryDrawer(e)">
<el-input
class="phoneInput"
v-model="e.value"
:placeholder="e.placeholder"
:type="e.type"
/>
<el-icon v-if="e.icon" class="rightArrow"> <ArrowRight /></el-icon>
</div>
</div>
<div class="quickBox" v-if="quickList.length > 0">
<span class="title">快捷电话</span>
<div
class="quickItem"
v-for="(quick, index) in quickList"
:key="index"
@click="setPhone(quick)"
>
<span>{{ quick.code }}</span>
<span style="margin-left: 5px">{{ quick.mobile }}</span>
</div>
</div>
</div>
</template>
<template #footer>
<div class="buttonBox">
<div class="left" @click.stop="resetClick">
<el-icon class="buttonIcon"><RefreshRight /></el-icon>
<span>重置</span>
</div>
<div class="right" @click.stop="confirmClick">
<el-icon><Check /></el-icon>
<span>保存</span>
</div>
</div>
</template>
</el-drawer>
<Country
:showCountryDrawer="showCountryDrawer"
@close="hanleCountryClose"
@confirmCountry="confirmCountry"
/>
</div>
</template>
<script setup name="PhoneDrawer">
import { watch, ref } from 'vue'
import Country from '@/views/components/country'
import deepClone from '@/utils/common'
const props = defineProps({
showDrawer: {
type: Boolean,
default: null
},
drawerInfo: {
type: Object,
default: () => {
return {}
}
},
phoneMenuList: {
type: Array,
default: () => {
return []
}
},
phoneQuickList: {
type: Array,
default: () => {
return []
}
}
})
const emit = defineEmits(['close', 'confirmDrawer'])
const { proxy } = getCurrentInstance()
const drawer = ref(false)
const showCountryDrawer = ref(false) //国家地区抽屉开关
const menuList = ref([])
const quickList = ref([])
const confirmCountry = item => {
menuList.value[0].value = item.areaCode
console.log('====================================')
console.log('menuList.value', menuList.value)
console.log('====================================')
hanleCountryClose()
}
const openCountryDrawer = item => {
if (item.arrow) {
showCountryDrawer.value = true
}
}
// 处理抽屉关闭前的事件
const handleBeforeClose = done => {
menuList.value = props.phoneMenuList
emit('close')
drawer.value = false
done()
}
const resetClick = () => {
menuList.value.forEach(e => {
e.value = ''
})
}
const confirmClick = () => {
let info = JSON.parse(JSON.stringify(props.drawerInfo))
//收集填写的电话传给父组件
let phone = {}
menuList.value.forEach(e => {
phone[e.key] = e.value
})
for (const key in props.drawerInfo) {
if (key === info.key) {
info[key] = phone
}
}
emit('confirmDrawer', info)
drawer.value = false
}
//通过快捷电话设置区号和电话号码
const setPhone = item => {
console.log('menuList', menuList.value)
console.log('item', item)
menuList.value[0].value = item.code
menuList.value[1].value = item.mobile
// menuList.value.forEach(e => {
// for (const key in item) {
// if (e.key === key) {
// e.value = item[e.key]
// }
// }
// })
}
// 关闭国家地区的抽屉
const hanleCountryClose = () => {
showCountryDrawer.value = false
}
// 初始化menuList数据
const initMenuList = () => {
// 使用深拷贝创建新的数组,避免修改props
menuList.value = deepClone(props.phoneMenuList)
}
watch(
() => props.showDrawer,
val => {
drawer.value = val
if (val) {
// 每次打开抽屉时初始化数据
nextTick(() => {
initMenuList()
})
}
}
)
watch(drawer, val => {
if (!val) {
proxy.$emit('close', false)
}
})
watch(
() => props.phoneQuickList,
val => {
quickList.value = val
},
{ immediate: true }
)
</script>
<style lang="scss" scoped>
::v-deep .el-drawer__header {
margin-bottom: 0 !important;
}
::v-deep .el-drawer__footer {
padding: 0;
}
.phoneInput ::v-deep .el-input__wrapper {
box-shadow: none !important;
}
.phoneInput ::v-deep .el-input__inner::placeholder {
text-align: right;
}
.phoneInput ::v-deep .el-input__inner {
text-align: right;
}
.phoneInput ::v-deep .el-input__wrapper {
padding: 0 !important;
}
.drawerContent {
.item {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #e5e6e8;
margin-bottom: 20px;
padding: 10px;
color: #4e5969;
font-size: 14px;
.right {
display: flex;
align-items: center;
}
.rightArrow {
color: #4e5969;
font-size: 14px;
}
}
.quickBox {
.title {
font-size: 18px;
border-left: 4px solid #165dff;
padding-left: 5px;
}
.quickItem {
display: flex;
align-items: center;
justify-content: flex-start;
margin-top: 10px;
color: #1d2129;
font-size: 14px;
border: 1px solid #e5e6e8;
border-radius: 3px;
padding: 10px;
}
}
}
.buttonBox {
box-shadow: 0 -1px 14px #00000014;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.left,
.right {
display: flex;
align-items: center;
justify-content: center;
width: 50%;
font-size: 16px;
height: 60px;
}
.left {
background-color: #fff;
color: #165dff;
.buttonIcon {
font-size: 16px;
color: #165dff;
}
}
.right {
background-color: #165dff;
color: #fff;
.buttonIcon {
font-size: 16px;
color: #fff;
}
}
}
</style>
<template>
<div>
<div v-if="processedCustomerData.length > 0">
<el-row>
<el-col :span="24">
<div class="topBtn">
<el-button type="warning" icon="DocumentAdd" @click="exportInfo"
>从已有列表中导入</el-button
>
<el-button
v-if="props.customerBizId"
type="primary"
icon="EditPen"
@click="handleEditStatus"
>编辑</el-button
>
</div>
</el-col>
</el-row>
<el-form ref="customerRef" :model="form" :rules="rules" label-width="120px">
<el-row v-for="father in processedCustomerData" style="margin-bottom: 10px">
<div class="formBox">
<div class="fatherLable">{{ father.fatherTitle }}</div>
<div class="fatherDes">{{ father.description }}</div>
<el-row>
<template v-for="child in father.data" :key="child.key">
<el-col :span="8" class="formItem" v-if="child.show">
<div>
<el-form-item :label="child.label" :prop="child.key" :key="child.key">
<el-input
v-if="child.type === 'Input'"
:type="child.inputType"
v-model="form[child.key]"
:placeholder="child.placeholder"
maxlength="30"
:disabled="child.disabled"
/>
<el-select
v-if="child.type === 'Select'"
v-model="form[child.key]"
:placeholder="child.placeholder"
@change="handleSelectChange(child)"
:disabled="child.disabled"
>
<el-option
v-for="item in child.options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-date-picker
style="width: 100%"
v-if="child.type === 'DatePicker'"
v-model="form[child.key]"
type="date"
:placeholder="child.placeholder"
:disabled="child.disabled"
/>
<el-input
v-if="child.type === 'arrowRight'"
v-model="form[child.key]"
:placeholder="child.placeholder"
@click="handleFoucs(child)"
:suffix-icon="ArrowRight"
readonly
:disabled="child.disabled"
>
</el-input>
</el-form-item>
</div>
</el-col>
</template>
</el-row>
</div>
</el-row>
<el-row>
<el-col>
<div class="tabButton">
<el-button
type="primary"
icon="RefreshRight"
@click="resetForm"
size="large"
:disabled="editStatus"
>取消</el-button
>
<el-button
type="primary"
icon="Check"
@click="submitForm"
size="large"
:disabled="editStatus"
>提交</el-button
>
</div>
</el-col>
</el-row>
</el-form>
<el-dialog title="客户信息" v-model="openList" width="600px" append-to-body>
<div>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="客户姓名" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入姓名"
clearable
@keyup.enter="customerList"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" circle @click="customerList" />
</el-form-item>
</el-form>
<div class="customerBox">
<div class="customerItem" v-for="item in tableData" :key="item.id">
<div class="top">
<div class="left">
<div>{{ item.name }}</div>
<div v-if="item.gender" style="margin: 0 5px">-</div>
<div v-if="item.gender" class="gender">
<dict-tag :options="sys_gender" :value="item.gender" />
</div>
</div>
<div class="right">
<el-button type="primary" size="small" @click="handleExport(item)"
>导入客户信息</el-button
>
</div>
</div>
<div class="bottom">
<div class="infoItem">
<span>证件类型:</span>
<span> <dict-tag :options="csf_id_type" :value="item.idType" /></span>
</div>
<div class="infoItem">
<span>证件号码:</span>
<span> {{ item.idCard || '暂无' }}</span>
</div>
<div class="infoItem">
<span>电话号码:</span>
<span> {{ item.phone || '暂无' }}</span>
</div>
<div class="infoItem">
<span>出生日期:</span>
<span> {{ formatToDate(item.birthdate) || '暂无' }}</span>
</div>
</div>
</div>
</div>
</div>
</el-dialog>
</div>
<Phone
@close="handleCloseDrawer"
:showDrawer="showPhoneDrawer"
:drawerInfo="drawerInfo"
:phoneMenuList="phoneMenuList"
:phoneQuickList="phoneQuickList"
@confirmDrawer="confirmDrawer"
/>
<Address
@close="handleCloseDrawer"
:showAddressDrawer="showAddressDrawer"
:drawerInfo="drawerInfo"
:addressMenuList="addressMenuList"
:addressQuickList="addressQuickList"
@confirmDrawer="confirmDrawer"
/>
<Country
:showCountryDrawer="showCountryDrawer"
@close="handleCloseDrawer"
@confirmCountry="confirmDrawer"
/>
</div>
</template>
<script setup name="customer">
import { ArrowRight } from '@element-plus/icons-vue'
import customerDomData from '@/formJson/customer'
import Country from '@/views/components/country'
import Phone from '@/views/components/phone'
import Address from '@/views/components/address'
import { getDicts } from '@/api/system/dict/data'
import { watch,nextTick } from 'vue'
import { addCustomer, getCustomerDetail, editCustomer, getCustomerList } from '@/api/sign/fna'
const props = defineProps({
activeName: { type: String, default: '' }, //tab名称
fearthStatus: { type: String, default: '' }, //父组件状态,新增、修改
customerBizId: { type: String, default: '' } //提交状态,新增、修改
})
const emit = defineEmits(['handleSuccess'])
const { proxy } = getCurrentInstance()
const { csf_id_type, sys_gender } = proxy.useDict('csf_id_type', 'sys_gender')
const showPhoneDrawer = ref(false) //电话抽屉开关
const showAddressDrawer = ref(false) //地址抽屉开关
const showCountryDrawer = ref(false) //国家/地区抽屉开关
const drawerInfo = ref({}) // 用于存储所有arrowRight类型的输入框输入值
const saveKey = ref({}) // 用于存储当前点击的drawer框返回的对象,修改的时候回显值也要存key
const errorFields = ref([]) // 存储校验失败的字段信息
const editStatus = ref(true) // 表单是否可编辑,若是修改初始不可编辑
const openList = ref(false) // 客户列表弹窗
const oldObjInfo = ref({}) // 修改时存储原始数据,便于撤销操作
const tableLoading = ref(false)
const tableData = ref([])
const total = ref(0)
// 地址组件菜单数据
const addressMenuList = ref([
{
name: '国家/地区',
icon: false,
value: '',
placeholder: '请输入国家/地区',
key: 'region'
},
{
name: '省市',
icon: false,
value: '',
placeholder: '请输入省市',
key: 'city'
},
{
name: '街道',
icon: false,
value: '',
placeholder: '请输入街道',
key: 'street'
},
{
name: '详细地址',
icon: false,
value: '',
placeholder: '请输入详细地址',
key: 'location'
}
])
// 快捷地址的数据
const addressQuickList = ref([])
const phoneMenuList = ref([
{
name: '区号',
icon: true,
value: '',
placeholder: '请输入区号',
arrow: true,
type: 'string'
},
{
name: '电话号码',
icon: false,
value: '',
placeholder: '请输入电话号码',
type: 'number'
}
])
const phoneQuickList = ref([])
const deleteKeyList = ref([
'objType',
'mailingAddress',
'residentialAddress',
'residenceAddress',
'companyAddress'
]) // 存储需要删除的key
const data = reactive({
form: {},
processedCustomerData: [], // 处理后的表单数据
oldCustomerData: [], // 保存旧的表单Dom,便于撤销操作
rules: {}, //表单验证规则,
queryParams: {
pageNo: 1,
pageSize: 4,
name: undefined
}
})
const { form, rules, processedCustomerData, queryParams, oldCustomerData } = toRefs(data)
const exportInfo = () => {
if (props.customerBizId && editStatus.value) {
proxy.$modal.confirm(`请先点击编辑在导入客户信息`, { showCancel: '0', title: '填写提示' })
return
}
openList.value = true
customerList()
}
const customerList = () => {
tableLoading.value = true
getCustomerList(queryParams.value).then(res => {
if (res.code == 200) {
tableData.value = res.data.records
total.value = res.data.total
tableLoading.value = false
}
})
}
// 从客户列表中导入客户信息到当前表单
const handleExport = row => {
oldObjInfo.value = JSON.parse(JSON.stringify(form.value)) // 修改时存储原始数据,便于撤销操作
setFormValue(row, processedCustomerData.value, true)
openList.value = false
}
// 获取字典数据
const fetchDictData = async dictType => {
try {
const resp = await getDicts(dictType)
return resp.data.map(p => ({ label: p.itemLabel, value: p.itemValue }))
} catch (error) {
console.error('获取字典数据失败:', error)
return []
}
}
// 添加英文校验函数
const validateEnglish = (rule, value, callback) => {
if (value && !/^[A-Za-z]*$/.test(value)) {
// 清空非英文字符
form.value.firstNamePinyin = ''
callback(new Error('只能输入英文字母'))
} else {
callback()
}
}
// 处理表单配置,添加字典数据
const processFormData = async () => {
// 深拷贝原始数据
const processedData = JSON.parse(JSON.stringify(customerDomData))
for (const section of processedData) {
if (section.data) {
for (const field of section.data) {
if (field.dictType) {
// 获取字典数据
field.options = await fetchDictData(field.dictType)
}
if (field.required) {
if (field.key === 'firstNamePinyin') {
rules.value[field.key] = [
{ required: true, message: `${field.label}不能为空`, trigger: 'blur' },
{ validator: validateEnglish, trigger: 'change' }
]
} else {
rules.value[field.key] = [
{ required: true, message: `${field.label}不能为空`, trigger: 'blur' }
]
}
}
if (props.customerBizId) {
field.disabled = true
} else {
field.disabled = false
}
}
}
}
if (props.customerBizId) {
getCustomerInfo(props.customerBizId, processedData)
} else {
editStatus.value = false
processedCustomerData.value = oldCustomerData.value = processedData
}
}
//弹出右侧抽屉
const handleFoucs = child => {
if (child.disabled) return
console.log('saveKey.value', saveKey.value)
drawerInfo.value = JSON.parse(JSON.stringify(child))
switch (child.drawerType) {
case 'phone':
phoneMenuList.value[0].key = child.code
phoneMenuList.value[1].key = child.key
phoneMenuList.value.forEach(item => {
for (const key in saveKey.value) {
if (key == child.key) {
for (const key2 in saveKey.value[key]) {
if (item.key == key2) {
item.value = saveKey.value[key][key2]
}
}
}
}
})
showPhoneDrawer.value = true
break
case 'address':
addressMenuList.value.forEach(item => {
for (const key in saveKey.value) {
if (key == child.key) {
for (const key2 in saveKey.value[key]) {
if (item.key == key2) {
item.value = saveKey.value[key][key2]
}
}
}
}
})
showAddressDrawer.value = true
break
case 'country':
showCountryDrawer.value = true
break
default:
break
}
}
// 处理抽屉关闭
const handleCloseDrawer = () => {
showPhoneDrawer.value = false
showAddressDrawer.value = false
showCountryDrawer.value = false
drawerInfo.value = {}
}
const confirmDrawer = info => {
// info 为抽屉返回的值
if (drawerInfo.value.type == 'arrowRight' && drawerInfo.value.drawerType) {
console.log('drawerInfo.value', drawerInfo.value)
let newObj = info[drawerInfo.value.key]
//要判断drawerType
switch (drawerInfo.value.drawerType) {
case 'phone':
newObj.objType = drawerInfo.value.drawerType
// 因为电话有多个,根据点击的电话类型在抽屉里回显,所以要用drawerInfo.value.code来控制
form.value[info.key] = `${newObj[drawerInfo.value.code]} ${newObj[drawerInfo.value.key]}`
saveKey.value[drawerInfo.value.key] = newObj
break
case 'address':
newObj.objType = drawerInfo.value.drawerType
newObj.type = drawerInfo.value.key
form.value[info.key] = `${newObj.region} ${newObj.city} ${newObj.street} ${newObj.location}`
saveKey.value[info.key] = newObj
break
case 'country':
info.objType = drawerInfo.value.drawerType
form.value[drawerInfo.value.key] = info.name
saveKey.value[drawerInfo.value.key] = info
break
default:
break
}
}
console.log('saveKey.value', saveKey.value)
handleCloseDrawer()
}
// 根据联动重置表单项的显示与否
const resetShow = (key, status) => {
for (const section of processedCustomerData.value) {
if (section.data) {
for (const field of section.data) {
if (field.key == key) {
// 获取字典数据
field.show = status
if (!status) {
form.value[key] = ''
}
}
}
}
}
}
const handleSelectChange = child => {
switch (child.key) {
case 'smoke':
// 选择吸烟,展示吸烟数量
if (form.value[child.key] == '1') {
resetShow('smokeQuantity', true)
} else {
resetShow('smokeQuantity', false)
}
break
default:
break
}
}
// 改变编辑状态
const handleEditStatus = () => {
editStatus.value = !editStatus.value
// 深拷贝原始数据
const processedData = JSON.parse(JSON.stringify(processedCustomerData.value))
for (const section of processedData) {
if (section.data) {
for (const field of section.data) {
if (editStatus.value) {
field.disabled = true
} else {
field.disabled = false
}
}
}
}
processedCustomerData.value = processedData
}
//给表单赋值 方便表单回显 obj 为表单数据
const setFormValue = (obj, formData, exportValue) => {
let tempPhoneList = []
let tempAddressList = []
addressQuickList.value = []
phoneQuickList.value = []
// 深拷贝原始数据
form.value = JSON.parse(JSON.stringify(obj))
// 深拷贝原始数据
const processedData = JSON.parse(JSON.stringify(formData))
for (const section of processedData) {
if (section.data) {
for (const field of section.data) {
if (obj.smokeQuantity && field.key == 'smokeQuantity') {
field.show = true
}
//要判断drawerType,因为抽屉要回显数据
switch (field.drawerType) {
case 'phone':
let phoneObj = {}
for (const key1 in field) {
for (const key2 in obj) {
if (key1 !== 'drawerType' && field[key1] == key2) {
if (key1 == 'code' && obj[key2]) {
phoneObj[field[key1]] = obj[key2].includes('+') ? obj[key2] : `+${obj[key2]}`
} else {
phoneObj[field[key1]] = obj[key2]
}
}
}
}
form.value[field.key] = `${phoneObj[field.code] || ''} ${phoneObj[field.key] || ''}`
phoneObj.phoneString = `${phoneObj[field.code] || ''} ${phoneObj[field.key] || ''}`
if (tempPhoneList.length > 0) {
tempPhoneList.forEach(item => {
if (item.phoneString !== phoneObj.phoneString) {
tempPhoneList.push(phoneObj)
}
})
} else {
tempPhoneList.push(phoneObj)
}
phoneObj.objType = field.drawerType
phoneObj.key = field.key
phoneObj.phoneCode = field.code
saveKey.value[field.key] = phoneObj
// phoneQuickList.value.push(phoneObj)
break
case 'address':
let addressObj = null
for (const key1 in field) {
if (obj.addressList && obj.addressList.length > 0) {
obj.addressList.forEach(item => {
if (key1 == item.type) {
addressObj = item
}
})
}
}
if (addressObj) {
form.value[
field.key
] = `${addressObj.region} ${addressObj.city} ${addressObj.street} ${addressObj.location}`
addressObj.addressString = `${addressObj.region} ${addressObj.city} ${addressObj.street} ${addressObj.location}`
addressObj.objType = field.drawerType
saveKey.value[field.key] = addressObj
if (tempAddressList.length > 0) {
tempAddressList.forEach(item => {
if (item.addressString !== addressObj.addressString) {
tempAddressList.push(addressObj)
}
})
} else {
tempAddressList.push(addressObj)
}
}
break
case 'country':
form.value[field.key] = obj.countryName
saveKey.value[field.key] = {
country: obj.country || '',
countryCode: obj.country || '',
countryName: obj.countryName || '',
name: obj.countryName || '',
objType: field.drawerType
}
break
default:
break
}
}
}
}
addressQuickList.value = tempAddressList
tempPhoneList.forEach(item => {
for (const key in saveKey.value) {
if (item.key == key) {
for (const key2 in saveKey.value[key]) {
if (item.key == key2) {
item.mobile = saveKey.value[key][key2]
}
if (item.phoneCode == key2) {
item.code = saveKey.value[key][key2]
}
}
}
}
})
phoneQuickList.value = removeDuplicates(tempPhoneList, 'phoneString')
addressQuickList.value = removeDuplicates(tempAddressList, 'addressString')
if (!exportValue) {
// 保存一份就得表单数据便于做撤销操作
oldObjInfo.value = JSON.parse(JSON.stringify(form.value))
}
processedCustomerData.value = oldCustomerData.value = processedData
}
// 数组去重
function removeDuplicates(arr, key) {
const seen = new Map()
const result = []
for (const item of arr) {
if (!seen.has(item[key])) {
seen.set(item[key], true)
result.push(item)
}
}
return result
}
// 获取校验失败的字段信息
const getInvalidFields = fields => {
const errors = []
for (const field in fields) {
if (fields[field] && fields[field].length > 0) {
errors.push({
field: field,
message: fields[field][0].message
})
}
}
return errors
}
// 表单提交
const submitForm = () => {
let submitObj = { ...form.value }
let addressList = []
submitObj['birthdate'] = proxy.formatToDate(submitObj['birthdate'])
proxy.$refs['customerRef'].validate((valid, fields) => {
if (valid) {
//处理表单数据
for (const key1 in form.value) {
for (const key2 in saveKey.value) {
//要判断drawerType
switch (saveKey.value[key2].objType) {
case 'phone':
if (key1 == key2) {
for (const key3 in saveKey.value[key2]) {
submitObj[key3] = saveKey.value[key2][key3]
}
}
break
case 'address':
if (key1 == key2) {
addressList.push(saveKey.value[key2])
}
break
case 'country':
if (key1 == key2) {
submitObj['country'] = saveKey.value[key2]['countryCode']
submitObj['countryName'] = saveKey.value[key2]['name']
}
break
default:
break
}
}
}
submitObj['birthdate'] = proxy.formatToDate(submitObj['birthdate'])
submitObj['addressList'] = addressList
// 删除多余字段
deleteKeyList.value.forEach(item => {
delete submitObj[item]
})
if (props.customerBizId) {
editCustomer(submitObj).then(res => {
if (res.code == 200) {
handleEditStatus()
proxy.$message.success('客户修改成功')
emit('handleSuccess', {
tab: 'customer',
customerBizId: props.customerBizId,
type: 'edit'
})
}
})
} else {
addCustomer(submitObj).then(res => {
if (res.code == 200) {
proxy.$message.success('客户新增成功')
emit('handleSuccess', {
tab: 'customer',
customerBizId: props.customerBizId,
type: 'add'
})
}
})
}
// emit('submitForm', submitObj)
errorFields.value = [] // 清空错误信息
} else {
// 获取校验失败的字段信息
errorFields.value = getInvalidFields(fields)
if (errorFields.value.length > 0) {
proxy.$message.error(errorFields.value[0].message)
}
}
})
}
const resetForm = () => {
proxy.$modal
.confirm('是否确认撤销所作操作?')
.then(function () {
if (props.customerBizId) {
form.value = { ...oldObjInfo.value }
} else {
resetShow('smokeQuantity', false)
proxy.$refs['customerRef'].resetFields()
}
processedCustomerData.value = JSON.parse(JSON.stringify(oldCustomerData.value))
console.log('processedCustomerData.value', processedCustomerData.value)
})
.catch(() => {})
}
// 获取流程详情
function getCustomerInfo(customerBizId, formData) {
getCustomerDetail(customerBizId).then(async res => {
if (res.code == 200) {
// 回显值
setFormValue(res.data, formData)
}
})
}
watch(
() => props.activeName,
newVal => {
if (newVal === 'customer') {
openList.value = false
processFormData()
}
}
)
</script>
<style lang="scss" scoped>
.topBtn {
width: 100%;
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.formBox {
width: 100%;
.fatherLable {
font-size: 18px;
border-left: 4px solid #165dff;
padding-left: 5px;
}
.fatherDes {
font-size: 14px;
color: #a8abb2;
margin-top: 5px;
margin-bottom: 20px;
}
.inputBox {
width: 100%;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 32px;
padding: 0px 11px;
.rightArrow {
font-size: 14px;
color: #a8abb2;
}
}
}
.tabButton {
box-shadow: 0 -1px 14px #00000014;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
/* padding-right: 20px; */
padding-top: 20px;
.sumbitBtn {
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
height: 60px;
background-color: #165dff;
color: #fff;
padding: 0 30px;
.buttonIcon {
font-size: 16px;
color: #fff;
}
}
}
.customerBox {
.customerItem {
padding: 10px;
border-radius: 5px;
margin-bottom: 20px;
box-shadow: 0 -1px 14px #00000014;
.top {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.left {
width: 40%;
display: flex;
align-items: center;
justify-content: flex-start;
.gender {
display: flex;
align-items: center;
}
}
}
.bottom {
.infoItem {
display: flex;
align-items: center;
margin: 5px 0;
}
}
}
.customerItem:last-child {
margin-bottom: 0px;
}
}
</style>
<template>
<div class="app-container">
<div>
<el-button type="primary" icon="Back" @click="handleBack" style="margin-bottom: 10px"
>返回</el-button
>
</div>
<el-card shadow="never">
<div class="cardHeader">
<div class="left">
<img class="iconBox" src="@/assets/images/icon1.png" alt="" />
<div class="rightBox">
<div class="num">{{ processInfo.fnaNo || '--' }}</div>
<div class="bottom">
<div class="status">{{ processInfo.status || '--' }}</div>
<div class="time">
<span class="iconfont icon-yanqiweiwancheng"></span>
<span
>{{ parseTime(processInfo.createTime) }}{{
processInfo.customerName || '--'
}}创建</span
>
</div>
</div>
</div>
</div>
<div class="right">
<el-button
:disabled="!(processInfo.customerBizId && processInfo.fnaFormBizId)"
type="success"
:icon="Check"
>{{ processInfo.appointmentBizId ? '已预约' : '立即预约' }}</el-button
>
<el-button
:disabled="!(processInfo.customerBizId && processInfo.fnaFormBizId)"
type="primary"
plain
>生成副本</el-button
>
</div>
</div>
<div class="tabsBox">
<el-tabs v-model="activeName" class="demo-tabs" :before-leave="beforeTabLeave">
<el-tab-pane v-for="tab in tabsList" :key="tab.name" :label="tab.label" :name="tab.name">
<div class="tabPaneBox">
<div v-if="tab.name === 'overview'" class="overviewBox">
<div
class="oneItem"
v-for="item in tabsList.filter(item => item.id !== -1 && item.status)"
:key="item.id"
@click="handleStep(item)"
>
<div
class="circle"
:class="{
finfishCircle: item.status == '1',
unFinishCircle: item.status == '0'
}"
>
<el-icon v-if="item.status == '1'"><Check /></el-icon>
<span v-else>{{ item.id }}</span>
</div>
<div
class="title"
:class="{
finfishTitle: item.status == '1',
unFinishTitle: item.status == '0'
}"
>
{{ item.title }}
</div>
<div
class="status"
:class="{
finfishStatus: item.status == '1',
unFinishStatus: item.status == '0'
}"
>
{{ item.status == '1' ? '已完成' : '未完成' }}
</div>
<div class="operation">
<el-icon class="editIcon" v-if="item.status == '0'"><EditPen /></el-icon>
<div
:class="{
finfishOperation: item.status == '1',
unFinishOperation: item.status == '0'
}"
>
{{ item.status == '1' ? '点击查看详情' : '去填写' }}
</div>
</div>
</div>
</div>
<Customer
v-if="tab.name === 'customer'"
:activeName="activeName"
:fearthStatus="route.query.type"
:customerBizId="processInfo.customerBizId"
@handleSuccess="handleSuccess"
/>
<div v-if="tab.name === 'fnaform'">FNA表单内容</div>
<div v-if="tab.name === 'appointment'">关联预约内容</div>
<div v-if="tab.name === 'newpolicy'">关联新单内容</div>
<div v-if="tab.name === 'policy'">关联保单内容</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</el-card>
</div>
</template>
<script setup name="FnaEdit">
import useUserStore from '@/store/modules/user'
import { addFna, getProcessDetail, updateProcess } from '@/api/sign/fna'
import Customer from './components/customer'
import { Check } from '@element-plus/icons-vue'
import { ref } from 'vue'
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const route = useRoute()
const router = useRouter()
const activeName = ref('overview')
const processInfo = ref({
fnaNo:'暂无',
status:'未完成',
createTime: proxy.parseTime(new Date()),
customerName: userStore.name
}) // 流程详情信息
const updateStatus = ref(false)
const tabsList = ref([
{
label: '总览',
name: 'overview',
id: -1
},
{
label: '客户资料',
name: 'customer',
id: 1,
status: '0',
key: 'customerBizId'
},
{
label: 'FNAform',
name: 'fnaform',
id: 2,
status: '0',
key: 'fnaFormBizId'
},
{
label: '关联预约',
name: 'appointment',
id: 3,
status: '0',
key: 'appointmentBizId'
},
{
label: '关联新单',
name: 'newpolicy',
id: 4,
status: '0',
key: 'policyBizld'
},
{
label: '关联保单',
name: 'policy',
id: 5,
status: '0',
key: 'policyNo'
}
])
const { csf_fna_status } = proxy.useDict('csf_fna_status')
// 更新流程
const processUpdate = (data, status) => {
updateProcess(data).then(async res => {
if (res.code == 200) {
updateStatus.value = false
// 获取流程详情
getProcessInfo(processInfo.value.fnaBizId, status)
}
})
}
// 新建流程
const getAddInfo = () => {
addFna({ remark: '' }).then(res => {
if (res.code == 200) {
// 获取流程详情
getProcessInfo(res.data.fnaBizId)
}
})
}
// 获取流程详情
function getProcessInfo(fnaBizId, changeTab) {
getProcessDetail(fnaBizId).then(res => {
if (res.code == 200) {
processInfo.value = res.data
tabsList.value.forEach(item => {
if (res.data[item.key] && item.status) {
item.status = '1'
} else {
item.status = '0'
}
})
csf_fna_status._object.csf_fna_status.forEach(item => {
if (item.value == res.data.status) processInfo.value.status = item.label
})
if (changeTab) {
activeName.value = changeTab
}
}
})
}
// Tab切换前的验证
const beforeTabLeave = (activeTabName, oldTabName) => {
// 如果切换到总览,始终允许
if (activeTabName === 'overview' || activeTabName === 'customer') {
return true
}
// 获取当前要切换到的tab
const targetTab = tabsList.value.find(tab => tab.name === activeTabName)
// 如果目标tab不存在,阻止切换
if (!targetTab) {
return false
}
// 检查前一项状态
const prevTabIndex = tabsList.value.findIndex(tab => tab.name === activeTabName) - 1
if (prevTabIndex >= 0) {
const prevTab = tabsList.value[prevTabIndex]
// 如果前一项存在且未完成,阻止切换
if (prevTab.status === '0') {
proxy.$modal.confirm(`请先填写${prevTab.label}`, { showCancel: '0', title: '填写提示' })
return false
}
}
return true
}
// 处理步骤点击
const handleStep = item => {
// 总览页可以直接切换
if (item.id === -1 || item.name === 'customer') {
activeName.value = item.name
return
}
// 检查前一项状态
const prevTabIndex = tabsList.value.findIndex(tab => tab.id === item.id) - 1
if (prevTabIndex >= 0) {
const prevTab = tabsList.value[prevTabIndex]
// 如果前一项存在且未完成,阻止切换
if (prevTab.status === '0') {
proxy.$modal.confirm(`请先填写${prevTab.label}`, { showCancel: false })
return
}
}
// 允许切换
activeName.value = item.name
}
const handleBack = () => {
router.go(-1)
}
if (route.query.type == 'add') {
// getAddInfo()
} else if (route.query.type == 'edit') {
getProcessInfo(route.query.fnaBizId)
}
const handleSuccess = info => {
switch (info.tab) {
case 'customer':
if (info.type == 'add') {
//客户提交成功,更新流程
processUpdate(
{ fnaBizId: processInfo.value.fnaBizId, customerBizId: info.customerBizId },
'fnaform'
)
} else {
processUpdate({ fnaBizId: processInfo.value.fnaBizId, customerBizId: info.customerBizId })
}
break
case 'fnaform':
break
case 'newpolicy':
break
case 'policy':
break
default:
break
}
}
// handleDomData()
// 1.通过新建流程进来的,要请求创建/csf/api/Fna/add拿到头部dom等信息
//2.每次进来要请求根据id获取/csf/api/Fna/get/vo,根据流程BizId获取进行到哪一步了,还可以拿到头部dom等信息
//3.每个模块保存完要重新请求/csf/api/Fna/get/vo,还要再请求/csf/api/Fna/update,更新后端数据库
</script>
<style lang="scss" scoped>
::v-deep .el-card {
border: none !important;
}
::v-deep .el-input-group__append,
.el-input-group__prepend {
background-color: #fff !important;
}
.app-container {
display: flex;
flex-direction: column;
min-height: calc(100vh - 84.5px);
padding: 20px;
background-color: #f2f3f5;
.cardHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
.left {
display: flex;
justify-content: flex-start;
align-items: center;
.iconBox {
width: 40px;
height: 40px;
margin-right: 5px;
background-color: #e8f3ff;
border-radius: 3px;
text-align: center;
padding: 5px;
}
.rightBox {
margin-left: 10px;
display: flex;
flex-direction: column;
.num {
font-size: 14px;
}
.bottom {
display: flex;
align-items: center;
margin-top: 3px;
.status {
font-size: 11px;
color: #00b42a;
background-color: #e8ffea;
padding: 4px 6px;
border-radius: 2px;
margin-right: 10px;
}
.time {
font-size: 12px;
color: #86909c;
.icon-yanqiweiwancheng {
margin-right: 5px;
font-size: 12px;
}
}
}
}
}
}
.tabsBox {
.tabButton {
box-shadow: 0 -1px 14px #00000014;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 20px;
padding-top: 20px;
.sumbitBtn {
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
height: 60px;
background-color: #165dff;
color: #fff;
padding: 0 30px;
.buttonIcon {
font-size: 16px;
color: #fff;
}
}
}
}
.tabPaneBox {
height: calc(100vh - 317px);
overflow-y: auto;
padding-right: 10px;
}
.overviewBox {
display: flex;
align-items: center;
justify-content: space-between;
.oneItem {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.circle {
width: 35px;
height: 35px;
box-sizing: border-box;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 5px;
}
.el-icon {
font-size: 16px;
font-weight: 600;
}
}
.finfishCircle {
background-color: #e8f3ff;
color: #165dff;
}
.todoCircle {
background-color: #165dff;
color: #fff;
}
.unFinishCircle {
background-color: #f2f3f5;
color: #4e5969;
}
.title {
font-size: 14px;
margin-bottom: 5px;
}
.finfishTitle {
color: #1d2129;
font-size: 14px;
}
.unFinishTitle {
color: #4e5969;
font-size: 13px;
}
.status {
font-size: 12px;
margin-bottom: 8px;
color: #86909c;
}
.finfishStatus {
color: #165dff;
}
.unFinishStatus {
color: #4e5969;
}
.operation {
display: flex;
align-items: center;
font-size: 12px;
.editIcon {
margin-right: 3px;
color: #165dff;
font-size: 12px;
}
}
.finfishOperation {
color: #4e5969;
}
.unFinishOperation {
color: #165dff;
}
}
}
</style>
<template>
<div class="app-container">
<!-- 步骤条 -->
<el-row style="margin-bottom: 30px">
<el-col :span="24">
<Step :stepList="stepList"></Step>
</el-col>
</el-row>
<!-- 条件查询 -->
<el-row>
<el-col :span="24">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="70px"
>
<el-form-item
><el-button type="primary" icon="Plus" @click="handleAdd"
>新建流程</el-button
></el-form-item
>
<el-form-item label="创建时间" style="width: 300px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item label="流程编号" prop="fnaNo">
<el-input
v-model="queryParams.fnaNo"
placeholder="请输入流程编号"
clearable
style="width: 150px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="客户姓名" prop="customerName">
<el-input
v-model="queryParams.customerName"
placeholder="请输入姓名"
clearable
style="width: 140px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择"
clearable
style="width: 110px"
>
<el-option
v-for="dict in csf_fna_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-table
v-loading="loading"
:data="tenantList"
@selection-change="tableSelect"
:default-sort="{ prop: 'createTime', order: 'descending' }"
>
<el-table-column type="selection" width="55" />
<el-table-column label="流程编号" align="center" prop="fnaNo" width="200" />
<el-table-column label="预约编号" align="center" prop="appointmentNo" width="150" />
<el-table-column label="新单编号" align="center" prop="policyId" />
<el-table-column label="保单号" align="center" prop="policyNo" />
<el-table-column label="客户姓名" align="center" prop="customerName" />
<el-table-column label="状态" align="center" width="150">
<template #default="scope">
<span v-if="scope.row.status == 'UNCOMPLETED'">
<span style="color: #ff7d00" class="iconfont icon-yanqiweiwancheng"></span> 未完成
</span>
<span v-if="scope.row.status == 'COMPLETED'"
><span style="color: #43cf7c" class="iconfont icon-yiwancheng"></span> 已完成
</span>
</template>
</el-table-column>
<!-- sortable 后端未做 -->
<el-table-column label="创建时间" align="center" prop="createTime">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="200"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
<!-- v-if="scope.row.status == 'COMPLETED'" -->
<el-button link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total >= 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 下一步到预约 -->
<div class="bottomBtn">
<el-button type="primary" @click="goToAppointment">下一步</el-button>
</div>
</div>
</template>
<script setup name="FnaList">
import Step from '@/views/components/moduleStep'
import useAppStore from '@/store/modules/app'
import {
changeUserStatus,
listTenant,
resetUserPwd,
delUser,
getTenant,
updateTenant,
addTenant,
changeTenantStatus
} from '@/api/system/tenant'
import { getFnaList, deleteFna } from '@/api/sign/fna'
import useUserStore from '@/store/modules/user'
const stepList = ref([
{ title: 'FNA', id: 1, finish: false, todo: true, unFinish: false },
{ title: '预约', id: 2, todo: false, finish: false, unFinish: true },
{ title: '新单跟进', id: 3, finish: false, todo: false, unFinish: true }
])
const userStore = useUserStore()
const router = useRouter()
const { proxy } = getCurrentInstance()
const { sys_status, csf_fna_status, sys_no_yes } = proxy.useDict(
'sys_status',
'csf_fna_status',
'sys_no_yes'
)
const tenantList = ref([])
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const dateRange = ref([])
const selectIdsList = ref([])
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 8,
startTime: '',
endTime: ''
}
})
const { queryParams, form, rules } = toRefs(data)
/** 多选框选中数据 */
function tableSelect(selection) {
selectIdsList.value = selection.map(item => item.projectBizId)
}
const goToAppointment = () => {
router.push({ path: '/sign/appointment' })
}
/** 查询租户列表 */
function getList() {
if (dateRange.value.length > 0) {
queryParams.value.startTime = `${dateRange.value[0]} 00:00:00`
queryParams.value.endTime = `${dateRange.value[1]} 23:59:59`
}
loading.value = true
try {
getFnaList(queryParams.value).then(response => {
tenantList.value = response.data.records
total.value = response.data.total
loading.value = false
})
} catch (error) {
tenantList.value = []
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNo = 1
getList()
}
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = []
proxy.resetForm('queryRef')
queryParams.value = {
pageNo: 1,
pageSize: 8,
startTime: '',
endTime: ''
}
handleQuery()
}
/** 删除按钮操作 */
function handleDelete(row) {
proxy.$modal
.confirm('是否确认删除' + row.customerName + '的FNA流程?')
.then(function () {
return deleteFna({ fnaBizId: row.fnaBizId })
})
.then(() => {
getList()
proxy.$modal.msgSuccess('删除成功')
})
.catch(() => {})
}
/** 租户状态修改 */
function handleStatusChange(row, event) {
let text = row.status === 0 ? '停用' : '启用'
const tenantName = row.tenantName
proxy.$modal
.confirm(`确认要${text}"${tenantName}"租户吗?`)
.then(() => changeTenantStatus({ tenantBizId: row.tenantBizId, status: row.status }))
.then(() => proxy.$modal.msgSuccess(`${text}成功`))
.catch(() => {
// 操作取消时恢复原状态
row.status = row.status === 0 ? 1 : 0
})
}
/** 重置操作表单 */
function reset() {
form.value = {
pageNo: 1,
pageSize: 8
}
proxy.resetForm('tenantRef')
}
/** 新增按钮操作 */
function handleAdd() {
router.push('/sign/FnaList/edit?type=add')
}
/** 修改按钮操作 */
function handleUpdate(row) {
router.push({ path: '/sign/FnaList/edit', query: { fnaBizId: row.fnaBizId, type: 'edit' } })
}
getList()
</script>
<style lang="scss" scoped>
/* ::v-deep .el-step__head.is-success {
color: none !important;
background-color: #e8f3ff !important;
border: none !important;
} */
/* ::v-deep .el-step__icon {
background-color: #e8f3ff !important;
} */
.bottomBtn {
width: 100%;
text-align: right;
margin-top: 20px;
}
</style>
<template>
<div class="app-container">
<!-- 步骤条 -->
<el-row style="margin-bottom: 30px">
<el-col :span="24">
<Step :stepList="stepList"></Step>
</el-col>
</el-row>
<!-- 条件查询 -->
<el-row>
<el-col :span="24">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="70px"
>
<el-form-item
><el-button type="primary" icon="Plus" @click="handleAdd"
>新建FNA</el-button
></el-form-item
>
<el-form-item label="创建时间" style="width: 300px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item label="FNA编号" prop="tenantName">
<el-input
v-model="queryParams.tenantName"
placeholder="请输入FNA编号"
clearable
style="width: 150px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="姓名" prop="contactName">
<el-input
v-model="queryParams.contactName"
placeholder="请输入姓名"
clearable
style="width: 100px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="租户状态"
clearable
style="width: 100px"
>
<el-option
v-for="dict in sys_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-table
v-loading="loading"
:data="tenantList"
@selection-change="tableSelect"
:default-sort="{ prop: 'createTime', order: 'descending' }"
>
<el-table-column type="selection" width="55" />
<el-table-column label="FNA编号" align="center" prop="tenantName" />
<el-table-column label="预约编号" align="center" prop="tenantName" />
<el-table-column label="新单编号" align="center" prop="contactName" />
<el-table-column label="保单号" align="center" prop="contactPhone" />
<el-table-column label="客户姓名" align="center" prop="maxProject" />
<el-table-column label="状态" align="center">
<template #default="scope">
<span v-if="scope.row.status == 0">
<span style="color: #ff7d00" class="iconfont icon-yanqiweiwancheng"></span> 未完成
</span>
<span v-if="scope.row.status == 1"
><span style="color: #43cf7c" class="iconfont icon-yiwancheng"></span> 已完成
</span>
</template>
</el-table-column>
<el-table-column label="创建时间" sortable align="center" prop="createTime">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="200"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button link type="primary" @click="handlePermission(scope.row)">查看</el-button>
<el-button link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
<el-button link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total >= 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 下一步到预约 -->
<div class="bottomBtn">
<el-button type="primary" @click="goToAppointment">下一步</el-button>
</div>
<!-- 添加或修改租户对话框 -->
<el-dialog :title="title" v-model="open" width="650px" append-to-body>
<el-form :model="form" :rules="rules" ref="tenantRef" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="租户名称" prop="tenantName">
<el-input v-model="form.tenantName" placeholder="请输入租户名称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系人姓名" prop="contactName">
<el-input v-model="form.contactName" placeholder="请输入联系人姓名" maxlength="30" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="联系电话" prop="contactPhone">
<el-input v-model="form.contactPhone" placeholder="请输入联系电话" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系邮箱" prop="contactEmail">
<el-input v-model="form.contactEmail" placeholder="请输入联系邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="所属行业" prop="industry">
<el-input v-model="form.industry" placeholder="请输入所属行业" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系地址" prop="address">
<el-input v-model="form.address" placeholder="请输入联系地址" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="最大项目数" prop="maxProject">
<el-input v-model="form.maxProject" placeholder="请输入最大项目数" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最大用户数" prop="maxUser">
<el-input v-model="form.maxUser" placeholder="请输入最大用户数" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="服务到期时间" prop="expireTime">
<el-date-picker
v-model="form.expireTime"
type="datetime"
placeholder="选择到期日期"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
/>
</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 sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>-->
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="租户LOGO">
<el-input v-model="form.logoUrl" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Appointment">
import Step from '@/views/components/moduleStep'
import { getToken } from '@/utils/auth'
import useAppStore from '@/store/modules/app'
import {
changeUserStatus,
listTenant,
resetUserPwd,
delUser,
getTenant,
updateTenant,
addTenant,
changeTenantStatus
} from '@/api/system/tenant'
import useUserStore from '@/store/modules/user'
const stepList = ref([
{ title: 'FNA', id: 1, finish: true, todo: false, unFinish: false },
{ title: '预约', id: 2, todo: true, finish: false, unFinish: false },
{ title: '新单跟进', id: 3, finish: false, todo: false, unFinish: true }
])
const userStore = useUserStore()
const router = useRouter()
const appStore = useAppStore()
const { proxy } = getCurrentInstance()
const { sys_status, sys_scope, sys_no_yes } = proxy.useDict('sys_status', 'sys_scope', 'sys_no_yes')
const tenantList = ref([])
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref('')
const dateRange = ref([])
const deptName = ref('')
const deptOptions = ref(undefined)
const enabledDeptOptions = ref(undefined)
const initPassword = ref(undefined)
const postOptions = ref([])
const roleOptions = ref([])
const selectIdsList = ref([])
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 8,
loginTenantBizId: userStore.currentTenant.apiLoginTenantInfoResponse.tenantBizId,
tenantName: undefined,
contactName: undefined,
contactPhone: undefined,
status: undefined
},
rules: {
tenantName: [{ required: true, message: '租户名称不能为空', trigger: 'blur' }],
contactEmail: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
contactPhone: [
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
}
})
const { queryParams, form, rules } = toRefs(data)
/** 多选框选中数据 */
function tableSelect(selection) {
selectIdsList.value = selection.map(item => item.projectBizId)
}
/** 查询租户列表 */
function getList() {
loading.value = true
listTenant(queryParams.value).then(response => {
tenantList.value = response.data.records
total.value = response.data.total
loading.value = false
})
}
/** 过滤禁用的部门 */
function filterDisabledDept(deptList) {
return deptList.filter(dept => {
if (dept.disabled) {
return false
}
if (dept.children && dept.children.length) {
dept.children = filterDisabledDept(dept.children)
}
return true
})
}
/** 节点单击事件 */
function handleNodeClick(data) {
queryParams.value.deptId = data.id
handleQuery()
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = []
proxy.resetForm('queryRef')
handleQuery()
}
/** 删除按钮操作 */
function handleDelete(row) {
const userIds = row.userId || ids.value
proxy.$modal
.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?')
.then(function () {
return delUser(userIds)
})
.then(() => {
getList()
proxy.$modal.msgSuccess('删除成功')
})
.catch(() => {})
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
'system/user/export',
{
...queryParams.value
},
`user_${new Date().getTime()}.xlsx`
)
}
/** 租户状态修改 */
function handleStatusChange(row, event) {
let text = row.status === 0 ? '停用' : '启用'
const tenantName = row.tenantName
proxy.$modal
.confirm(`确认要${text}"${tenantName}"租户吗?`)
.then(() => changeTenantStatus({ tenantBizId: row.tenantBizId, status: row.status }))
.then(() => proxy.$modal.msgSuccess(`${text}成功`))
.catch(() => {
// 操作取消时恢复原状态
row.status = row.status === 0 ? 1 : 0
})
}
/** 更多操作 */
function handleCommand(command, row) {
switch (command) {
case 'handleResetPwd':
handleResetPwd(row)
break
case 'handleAuthRole':
handleAuthRole(row)
break
default:
break
}
}
/** 跳转角色分配 */
function handleAuthRole(row) {
const userId = row.userId
router.push('/system/user-auth/role/' + userId)
}
/** 重置密码按钮操作 */
function handleResetPwd(row) {
proxy
.$prompt('请输入"' + row.userName + '"的新密码', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
inputValidator: value => {
if (/<|>|"|'|\||\\/.test(value)) {
return '不能包含非法字符:< > " \' \\\ |'
}
}
})
.then(({ value }) => {
resetUserPwd(row.userId, value).then(response => {
proxy.$modal.msgSuccess('修改成功,新密码是:' + value)
})
})
.catch(() => {})
}
/** 选择条数 */
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.userId)
single.value = selection.length != 1
multiple.value = !selection.length
}
/** 下载模板操作 */
function importTemplate() {
proxy.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`)
}
/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
upload.isUploading = true
}
/** 文件上传成功处理 */
const handleFileSuccess = (response, file, fileList) => {
upload.open = false
upload.isUploading = false
proxy.$refs['uploadRef'].handleRemove(file)
proxy.$alert(
"<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
response.msg +
'</div>',
'导入结果',
{ dangerouslyUseHTMLString: true }
)
getList()
}
/** 提交上传文件 */
function submitFileForm() {
proxy.$refs['uploadRef'].submit()
}
/** 重置操作表单 */
function reset() {
form.value = {
tenantBizId: undefined,
tenantName: undefined,
logoUrl: undefined,
contactName: undefined,
contactPhone: undefined,
contactEmail: undefined,
industry: undefined,
address: undefined,
status: '0',
expireTime: undefined,
maxProject: undefined,
maxUser: undefined
}
proxy.resetForm('tenantRef')
}
/** 取消按钮 */
function cancel() {
open.value = false
reset()
}
/** 新增按钮操作 */
function handleAdd() {
reset()
open.value = true
title.value = '添加租户'
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset()
const tenantBizId = row.tenantBizId
getTenant(tenantBizId).then(response => {
form.value = response.data
open.value = true
title.value = '修改用户'
})
}
/** 分配权限 */
function handlePermission(row) {
router.push(`/system/tenant/permission?tenantBizId=` + row.tenantBizId)
}
/** 新增或者修改租户提交按钮 */
function submitForm() {
proxy.$refs['tenantRef'].validate(valid => {
if (valid) {
if (form.value.tenantBizId != undefined) {
updateTenant(form.value).then(response => {
proxy.$modal.msgSuccess('修改成功')
open.value = false
getList()
})
} else {
addTenant(form.value).then(response => {
proxy.$modal.msgSuccess('新增成功')
open.value = false
getList()
})
}
}
})
}
getList()
</script>
<style lang="scss" scoped>
/* ::v-deep .el-step__head.is-success {
color: none !important;
background-color: #e8f3ff !important;
border: none !important;
} */
/* ::v-deep .el-step__icon {
background-color: #e8f3ff !important;
} */
.bottomBtn {
width: 100%;
text-align: right;
margin-top: 20px;
}
</style>
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
v-if="(userStore.isSuperAdmin === 0 && scope.row.scope !== 1) || userStore.isSuperAdmin === 1">分配权限</el-button> v-if="(userStore.isSuperAdmin === 0 && scope.row.scope !== 1) || userStore.isSuperAdmin === 1">分配权限</el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
v-if="(userStore.isSuperAdmin === 0 && scope.row.scope !== 1) || userStore.isSuperAdmin === 1">修改</el-button> v-if="(userStore.isSuperAdmin === 0 && scope.row.scope !== 1) || userStore.isSuperAdmin === 1">修改</el-button>
<!-- <el-button link type="primary" icon="View" @click="handleUpdate(scope.row)" >详情</el-button>-->
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-if="(userStore.isSuperAdmin === 0 && scope.row.scope !== 1) || userStore.isSuperAdmin === 1">删除</el-button> v-if="(userStore.isSuperAdmin === 0 && scope.row.scope !== 1) || userStore.isSuperAdmin === 1">删除</el-button>
</template> </template>
......
<template> <template>
<div class="app-container workbench-container"> <div class="app-container">
<el-card class="box-card" shadow="never"> <div class="workHeader">
<template #header> <el-row>
<div class="card-header"> <el-col :sm="12" :lg="12" :xs="24">
<span>我的应用</span> <div class="headerLeft">
<el-tag type="primary">{{ currentTenantName }}</el-tag> <div class="top">欢迎!</div>
</div> <div class="bottom">王力群,wangliqun@bytedance.com</div>
</template> </div>
</el-col>
<el-col :sm="12" :lg="12" :xs="24" class="right">
<div class="headerRight">
<div class="item" v-for="data in headerList" :key="data.id">
<div class="top">{{ data.title }}</div>
<div class="bottom">
<div class="iconfont headerIcon" :class="data.icon"></div>
{{ data.count }}
</div>
</div>
</div>
</el-col>
</el-row>
</div>
<div class="app-list"> <div class="workContent" :style="{ padding: gap }">
<AppCard <el-row>
v-for="project in projects" <!-- 左侧内容 -->
:key="project.projectBizId" <el-col :sm="16" :lg="18" :style="{ paddingRight: gap }">
:project="project" <el-card shadow="never" class="leftOneCard" :style="{ marginBottom: gap }">
/> <div class="leftOneCardContent">
</div> <div class="item" v-for="data in staticList" :key="data.id">
<div class="top">{{ data.title }}</div>
<div v-if="!projects.length" class="empty-container"> <div class="bottom">
<el-empty description="暂无可用应用" /> <div
</div> class="iconfont staticIcon"
</el-card> :class="data.icon"
:style="{ background: data.color }"
></div>
<div>{{ data.count }}{{ data.unit }}</div>
</div>
</div>
</div>
</el-card>
<el-row>
<!-- 左侧内容 -->
<el-col :sm="12" :lg="12">
<el-card shadow="never" class="leftTwoCard" :style="{ marginBottom: gap }">
<template #header>
<div>
<span class="commonCardTitle">工单处理</span>
</div>
</template>
<div class="leftTwoCardContent">
<div class="item" v-for="data in workList" :key="data.id">
<div class="left">
<div class="unit" :style="{ background: data.color }">{{ data.unit }}</div>
<div class="title">
<div class="oneTit">{{ data.title }}</div>
<div class="subTit">{{ data.subTitle }}</div>
</div>
</div>
<div class="right">
<el-icon style="margin-right: 10px"><EditPen /></el-icon>
<el-icon><Delete /></el-icon>
</div>
</div>
</div>
<template #footer>
<div class="leftTwoFooter">
加载更多
<el-icon><ArrowDown /></el-icon>
</div>
</template>
</el-card>
<el-card shadow="never" class="leftThreeCard">
<template #header>
<div class="leftThreeHeader">
<span class="commonCardTitle">最新资讯</span>
<span class="quitTit commonCardTitle">所有项目</span>
</div>
</template>
<div class="leftThreeCardContent">
<div class="item" v-for="data in infoList" :key="data.id">
<div class="top">
<div class="left">
<img class="iconBox" src="@/assets/images/icon1.png" alt="" />
<div class="title">{{ data.title }}</div>
</div>
<el-icon class="arrow"><ArrowRight /></el-icon>
</div>
<div class="bottom">{{ data.subTitle }}</div>
</div>
</div>
</el-card>
</el-col>
<!-- 右侧内容 -->
<el-col :sm="12" :lg="12" :style="{ paddingLeft: gap }">
<el-card shadow="never" class="tableCard">
<template #header>
<div>
<span class="commonCardTitle">财务需求分析记录</span>
</div>
</template>
<div class="recordTable">
<div class="timeBox">
<div v-for="time in timeList">
<div
:style="{ borderRight: time.line ? '2px solid #E0E0E0' : '' }"
class="timeTitle"
:class="{ active: currentTimeId == time.id }"
>
{{ time.title }}
</div>
</div>
</div>
<div class="tableContent">
<div class="emptyBox">
<img class="emptyImg" src="@/assets/images/empty.png" alt="" />
<div class="emptyTit">暂无数据</div>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</el-col>
<!-- 右侧内容 -->
<el-col :sm="8" :lg="6">
<el-card shadow="never" class="rightOneCard">
<template #header>
<div>
<span class="commonCardTitle">快捷操作</span>
</div>
</template>
<div class="rightOneCardContent">
<div class="item" v-for="data in quickList" :key="data.id">
<div class="top">
<div class="iconfont quickIcon" :class="data.icon"></div>
</div>
<div class="bottom">
{{ data.title }}
</div>
</div>
</div>
</el-card>
<el-carousel class="rightTwoCard" :interval="5000" arrow="always" autoplay>
<el-carousel-item v-for="item in 4" :key="item">
<h3 text="2xl" justify="center">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
<el-card shadow="never" class="rightThreeCard">
<template #header>
<div>
<span class="commonCardTitle">文档</span>
</div>
</template>
<div class="threeCardContent">
<div class="item" v-for="data in documentList" :key="data.id">
<div class="bottom">
{{ data.title }}
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</div> </div>
</template> </template>
...@@ -27,29 +172,551 @@ ...@@ -27,29 +172,551 @@
import { computed, watch } from 'vue' import { computed, watch } from 'vue'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import AppCard from './components/AppCard.vue' import AppCard from './components/AppCard.vue'
import { fa } from 'element-plus/es/locales.mjs'
const userStore = useUserStore() const userStore = useUserStore()
const headerList = ref([
{
id: '1',
title: '总保费(HKD)',
icon: 'icon-zongbaofei',
count: '392.52',
url: '/todo'
},
{
id: '2',
title: '有效保单数',
icon: 'icon-youxiaobaodan',
count: '0.00',
url: '/todo'
},
// 当前租户名称 {
const currentTenantName = computed(() => { id: '3',
return userStore.currentTenant.apiLoginTenantInfoResponse?.tenantName || '未选择租户' title: '待来佣保单',
}) icon: 'icon-daijiesuan',
count: '10',
// 获取项目列表 url: '/todo'
const projects = computed(() => { }
return userStore.currentTenant.apiLoginProjectInfoResponseList || [] ])
}) const quickList = ref([
{
id: '1',
title: '财务中心',
icon: 'icon-huabi',
url: '/todo'
},
{
id: '2',
title: '预约信息',
icon: 'icon-yuyue',
url: '/todo'
},
{
id: '3',
title: '新单跟进',
icon: 'icon-genjinjilu',
url: '/todo'
},
{
id: '4',
title: '保单查询',
icon: 'icon-jianchafudao',
url: '/todo'
},
{
id: '5',
title: '续期管理',
icon: 'icon-xuqi',
url: '/todo'
},
{
id: '6',
title: '客户管理',
icon: 'icon-anquan',
url: '/todo'
},
// 监听租户变化 {
watch( id: '7',
() => userStore.currentTenant, title: '财务需求分析',
() => { icon: 'icon-xiexian',
// 租户变化时自动刷新 url: '/todo'
} }
) ])
const documentList = ref([
{
id: '1',
title: '使用手册',
icon: 'el-icon-s-order',
url: '/todo'
},
{
id: '2',
title: '业务流程',
icon: 'el-icon-s-order',
url: '/todo'
},
{
id: '3',
title: '公司介绍',
icon: 'el-icon-s-order',
url: '/todo'
},
{
id: '4',
title: '联系方式',
icon: 'el-icon-s-order',
url: '/todo'
}
])
const staticList = ref([
{
id: '1',
title: '财务需求分析数',
icon: 'icon-xiexian',
count: '3735',
unit: '条',
url: '/todo',
color: '#F53F3F'
},
{
id: '2',
title: '预约单数',
icon: 'icon-xiexian',
count: '12',
unit: '单',
url: '/todo',
color: '#0FC6C2'
},
{
id: '3',
title: '新单数',
icon: 'icon-xiexian',
count: '12',
unit: '单',
url: '/todo',
color: '#165DFF'
},
{
id: '4',
title: '续期保单数',
icon: 'icon-xiexian',
count: '13',
unit: '份',
url: '/todo',
color: '#0FC6C2'
}
])
const infoList = ref([
{
id: '1',
title: '产品优惠',
subTitle: 'XXXXX第四季度优惠政策'
},
{
id: '2',
title: '产品培训',
subTitle: 'XXXXX产品培训回放'
},
{
id: '3',
title: '重要通知',
subTitle: 'XXXXX保单未续费'
},
{
id: '4',
title: '系统消息',
subTitle: 'FNA导出成功!'
}
])
const timeList = ref([
{
id: '1',
title: '本月',
line: true
},
{
id: '2',
title: '近三个月',
line: true
},
{
id: '3',
title: '近一年',
line: false
}
])
const workList = ref([
{
id: '1',
title: '新单跟进',
subTitle: '张三预约单待录入',
unit: 'Q',
color: '#7BC616'
},
{
id: '2',
title: '续费提醒',
subTitle: 'bug fix',
unit: 'Y',
color: '#14C9C9'
},
{
id: '3',
title: '预约单生成',
subTitle: '车险预约单已生成',
unit: 'N',
color: '#168CFF'
}
])
const currentTimeId = ref('1')
const gap = ref('10px')
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep .el-card {
border: none !important;
}
::v-deep .el-carousel__button {
width: 10px !important ;
}
.app-container {
display: flex;
flex-direction: column;
min-height: calc(100vh - 84.5px);
padding: 0;
background-color: #f2f3f5;
.workHeader {
background-color: #fff;
width: 100%;
flex-shrink: 0;
padding: 0 20px 20px 20px;
border-bottom: 1px solid #e0e0e0;
color: #4e5969;
.headerLeft {
margin-top: 20px;
.top {
font-size: 24px;
font-weight: 600;
margin-bottom: 5px;
}
.bottom {
font-size: 14px;
color: rgba(134, 144, 156, 1);
}
}
.headerRight {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 20px;
.item {
display: flex;
flex-direction: column;
margin-right: 20px;
.top {
margin-bottom: 7px;
font-size: 14px;
}
.bottom {
font-size: 24px;
font-weight: 600;
color: #1d2129;
display: flex;
align-items: center;
.headerIcon {
margin-right: 5px;
color: #4e5969;
font-size: 14px;
}
}
}
.item:last-child {
margin-right: 0;
}
}
}
.workContent {
width: 100%;
flex: 1;
/* padding: 16px; */
background-color: rgba(235, 238, 246, 0.5);
overflow: auto;
.commonCardTitle {
font-size: 15px;
font-weight: normal;
}
.rightOneCard {
.rightOneCardContent {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
.item {
width: 33%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 10px;
.top {
background-color: #0fc6c2;
border-radius: 5px;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 3px;
.quickIcon {
color: #fff;
font-size: 14px;
}
}
.bottom {
color: #4e5969;
font-size: 10px;
}
}
.item:last-child {
margin-bottom: 0px;
}
}
}
.rightTwoCard {
height: 180px;
margin: 16px 0;
border-radius: 3px;
.el-carousel__item h3 {
color: #475669;
opacity: 0.75;
line-height: 300px;
margin: 0;
text-align: center;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
background-color: #d3dce6;
}
}
.rightThreeCard {
.threeCardContent {
display: flex;
flex-direction: column;
.item {
font-size: 14px;
color: #86909c;
margin-bottom: 8px;
}
.item:last-child {
margin-bottom: 0px;
}
}
}
.leftOneCard {
/* margin-bottom: 16px; */
.leftOneCardContent {
display: flex;
justify-content: space-between;
.item {
display: flex;
flex-direction: column;
.top {
margin-bottom: 7px;
font-size: 12px;
color: #4e5969;
}
.bottom {
font-size: 20px;
font-weight: 600;
color: #1d2129;
display: flex;
align-items: center;
justify-content: center;
.staticIcon {
margin-right: 5px;
color: #fff;
font-size: 8px;
border-radius: 3px;
padding: 3px;
}
}
}
}
}
.leftTwoCard {
/* margin-bottom: 16px; */
.leftTwoCardContent {
.item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
.left {
display: flex;
justify-content: flex-start;
align-items: center;
.unit {
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
color: #fff;
font-size: 12px;
}
.title {
.oneTit {
font-size: 12px;
color: #1d2129;
}
.subTit {
font-size: 11px;
color: #4e5969;
margin-top: 5px;
width: 300px; /* 设置宽度 */
white-space: nowrap; /* 禁止换行 */
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 显示省略号 */
}
}
}
}
.item:last-child {
margin-bottom: 0px;
}
}
.leftTwoFooter {
font-size: 12px;
color: #165dff;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
}
.leftThreeCard {
.leftThreeHeader {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.quitTit {
color: #165dff;
font-size: 13px;
}
}
.leftThreeCardContent {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
.item {
width: 50%;
padding-bottom: 16px;
.top {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.left {
display: flex;
align-items: center;
justify-content: flex-start;
.iconBox {
width: 20px;
height: 20px;
margin-right: 5px;
}
.title {
font-size: 12px;
color: #1d2129;
}
}
.arrow {
font-size: 12px;
color: #4e5969;
font-weight: bold;
}
}
.bottom {
font-size: 12px;
color: #4e5969;
margin-top: 10px;
width: 230px; /* 设置宽度 */
white-space: nowrap; /* 禁止换行 */
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 显示省略号 */
}
}
.item:nth-child(2n + 1) {
padding-right: 10px;
border-right: 1px solid #e4e6eb;
}
.item:first-child {
border-bottom: 1px solid #e4e6eb;
}
.item:nth-child(2n) {
padding-left: 16px;
}
.item:last-child {
border-top: 1px solid #e4e6eb;
}
.item:nth-last-child(-n + 2) {
padding-top: 16px;
}
}
}
.tableCard {
.recordTable {
width: 100%;
.timeBox {
display: flex;
align-items: center;
justify-content: flex-start;
.timeTitle {
padding: 0 10px;
font-size: 13px;
color: #4e5969;
&.active {
color: #165dff;
}
}
}
.tableContent {
width: 100%;
.emptyBox {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 300px;
.emptyImg {
height: 80px;
}
.emptyTit {
font-size: 14px;
color: #4e5969;
margin-top: 10px;
}
}
}
}
}
}
}
.workbench-container { .workbench-container {
padding: 20px; padding: 20px;
background-color: #f0f2f5; background-color: #f0f2f5;
...@@ -74,4 +741,74 @@ watch( ...@@ -74,4 +741,74 @@ watch(
.empty-container { .empty-container {
padding: 40px 0; padding: 40px 0;
} }
@media only screen and (max-width: 768px) {
.workHeader {
.headerRight {
justify-content: flex-start !important;
}
}
}
@media only screen and (min-width: 768px) {
.workHeader {
.headerRight {
justify-content: flex-end !important;
}
}
.workContent {
.leftTwoCard {
.leftTwoCardContent {
.item {
.left {
.title {
.subTit {
width: 120px !important;
}
}
}
}
}
}
.leftThreeCard {
.leftThreeCardContent {
.item {
.bottom {
width: 100px !important; /* 设置宽度 */
}
}
}
}
}
}
@media only screen and (min-width: 1200px) {
.workHeader {
.headerRight {
justify-content: flex-end !important;
}
}
.workContent {
.leftThreeCard {
.leftThreeCardContent {
.item {
.bottom {
width: 230px !important; /* 设置宽度 */
}
}
}
}
.leftTwoCard {
.leftTwoCardContent {
.item {
.left {
.title {
.subTit {
width: 300px !important;
}
}
}
}
}
}
}
}
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment