持续集成分支
This commit is contained in:
125
src/components/AiAreaPicker.vue
Normal file
125
src/components/AiAreaPicker.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<section class="AiAreaPicker">
|
||||
<div @click="handleOpenDialog">
|
||||
<slot v-if="$scopedSlots.default" :selected="selected"/>
|
||||
<el-button v-else type="text">选择地区</el-button>
|
||||
</div>
|
||||
<ai-dialog :visible.sync="dialog" title="选择地区" @onConfirm="submit" @close="selecting=[],init()" destroy-on-close>
|
||||
<el-breadcrumb separator-class="el-icon-arrow-right">
|
||||
<el-breadcrumb-item v-for="(item,i) in path" :key="item.id">
|
||||
<el-button type="text" @click.native="handlePathClick(i)">{{ item.name }}</el-button>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
<ai-table-select class="mar-t16" v-model="selecting" :instance="instance" :action="action" :isShowPagination="false" extra="hidden" search-key="name"
|
||||
multiple valueObj>
|
||||
<template slot="extra" slot-scope="{row}">
|
||||
<el-button v-if="row.id!=root" type="text" icon="el-icon-arrow-right" @click.stop="getChildren(row)"/>
|
||||
</template>
|
||||
</ai-table-select>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "AiAreaPicker",
|
||||
model: {
|
||||
prop: "value",
|
||||
event: "change"
|
||||
},
|
||||
props: {
|
||||
value: {default: ""},
|
||||
meta: {default: null},
|
||||
root: {default: ""},
|
||||
instance: {type: Function, required: true},
|
||||
multiple: Boolean
|
||||
},
|
||||
computed: {
|
||||
action() {
|
||||
let currentParent = this.path.slice(-1)?.[0]?.id
|
||||
return !!currentParent && /[^0]0{0,2}$/.test(currentParent) ? `/admin/appresident/queryAreaIdGroup?currentAreaId=${currentParent}` :
|
||||
`/admin/area/queryAreaByParentIdSelf?self=${currentParent == this.root ? 1 : ''}&id=${currentParent}`
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(v) {
|
||||
this.dispatch('ElFormItem', 'el.form.change', [v]);
|
||||
},
|
||||
selected(v) {
|
||||
this.$emit("update:meta", v)
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
options: [],
|
||||
selecting: [],
|
||||
path: [],
|
||||
selected: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dispatch(componentName, eventName, params) {
|
||||
let parent = this.$parent || this.$root;
|
||||
let name = parent.$options.componentName;
|
||||
while (parent && (!name || name !== componentName)) {
|
||||
parent = parent.$parent;
|
||||
if (parent) {
|
||||
name = parent.$options.componentName;
|
||||
}
|
||||
}
|
||||
if (parent) {
|
||||
parent.$emit.apply(parent, [eventName].concat(params));
|
||||
}
|
||||
},
|
||||
getChildren(row) {
|
||||
let area = this.path.find(e => e.id == row.id)
|
||||
if (!area) this.path.push(row)
|
||||
},
|
||||
handlePathClick(index) {
|
||||
this.path.splice(index + 1, 10)
|
||||
},
|
||||
submit() {
|
||||
this.$emit("change", this.selecting.map(e => e.id))
|
||||
this.selected = this.$copy(this.selecting)
|
||||
this.$emit("select", this.selecting)
|
||||
this.dialog = false
|
||||
},
|
||||
init() {
|
||||
this.path = [{id: this.root, name: "可选范围"}]
|
||||
},
|
||||
handleOpenDialog() {
|
||||
let areas = this.value.filter(e => /^\d{12}$/.test(e))
|
||||
this.instance.post("/admin/area/getAreaNameByids", null, {
|
||||
params: {ids: areas?.toString()}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.selecting = this.value.map(id => ({id, name: res.data?.[id] || id}))
|
||||
this.dialog = true
|
||||
}
|
||||
})
|
||||
},
|
||||
getAreaById(id) {
|
||||
return this.instance.post("/admin/area/queryAreaByAreaid", null, {
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
return res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiAreaPicker {
|
||||
.mar-t16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
181
src/components/AppLicence.vue
Normal file
181
src/components/AppLicence.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<section class="AppLicence" v-if="showLicence">
|
||||
<ai-list>
|
||||
<template #content>
|
||||
<div class="licence-content">
|
||||
<img class="left-img" src="https://cdn.cunwuyun.cn/dvcp/key.png" alt="" />
|
||||
<div class="content-right">
|
||||
<h3 class="title">产品许可信息</h3>
|
||||
<p class="mini-title">您当前的版本为Saas专业版,非常感谢您对我们产品的认可与支持!</p>
|
||||
<div class="info">
|
||||
<span class="label">过期时间</span>
|
||||
<span class="value color-f46" v-if="info.isExpired == 1">{{info.expireDate ? info.expireDate+'(已过期)' : '未激活'}}</span>
|
||||
<span class="value color-26f" v-else>{{info.expireDate}}</span>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span class="label">主板序列号</span>
|
||||
<span class="value">{{info.mainBoard}}</span>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span class="label">CPU</span>
|
||||
<span class="value">{{info.cpu}}</span>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span class="label">MAC地址</span>
|
||||
<span class="value">{{info.mac}}</span>
|
||||
</div>
|
||||
<div class="info mar-b32">
|
||||
<span class="label">IP地址</span>
|
||||
<span class="value">{{info.ip}}</span>
|
||||
</div>
|
||||
<el-upload
|
||||
class="upload-demo mar-r16"
|
||||
action
|
||||
multiple
|
||||
accept=".lic"
|
||||
:http-request="uploadFile">
|
||||
<div class="btn">上传许可</div>
|
||||
</el-upload>
|
||||
<div class="btn" @click="showLicence = false">返回登录</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ai-list>
|
||||
</section>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "AppLicence",
|
||||
label: "产品许可",
|
||||
props: {
|
||||
instance: Function,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
info: {},
|
||||
showLicence: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.showLicence = true
|
||||
this.getDetail()
|
||||
},
|
||||
hide() {
|
||||
this.showLicence = false
|
||||
},
|
||||
getDetail() {
|
||||
this.instance.post(`/admin/license/detail`).then(res => {
|
||||
if (res?.data) {
|
||||
this.info = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
uploadFile: function (file) {
|
||||
let formData = new FormData();
|
||||
formData.append("file", file.file);
|
||||
this.instance.post(`/admin/license/save`, formData, {withoutToken: false}).then(res => {
|
||||
if (res && res.code == 0) {
|
||||
this.$message.success("证书上传成功!");
|
||||
this.getDetail()
|
||||
}
|
||||
}).catch((err) => {
|
||||
this.$message.error(err);
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.AppLicence {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
:deep( .ai-list){
|
||||
background-color: #F5F6F9;
|
||||
}
|
||||
:deep( .ai-list .ai-list__content--right .ai-list__content--right-wrapper){
|
||||
background-color: #F5F6F9;
|
||||
box-shadow: 0 0 0 0;
|
||||
margin: 0!important;
|
||||
padding: 0!important;
|
||||
}
|
||||
:deep( .el-upload-list){
|
||||
display: none;
|
||||
}
|
||||
.licence-content{
|
||||
display: flex;
|
||||
width: 1000px;
|
||||
margin: 200px auto 0;
|
||||
.left-img{
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
.content-right{
|
||||
width: 800px;
|
||||
.title{
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
line-height: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.mini-title{
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 22px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.info{
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
color: #222;
|
||||
.label{
|
||||
width: 164px;
|
||||
}
|
||||
.value{
|
||||
width: calc(100% - 164px);
|
||||
}
|
||||
.color-26f{
|
||||
color: #26f;
|
||||
}
|
||||
.color-f46{
|
||||
color: #f46;
|
||||
}
|
||||
}
|
||||
.mar-b32{
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.upload-demo{
|
||||
display: inline-block;
|
||||
}
|
||||
.btn{
|
||||
display: inline-block;
|
||||
width: 88px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background: linear-gradient(90deg, #299FFF 0%, #0C61FF 100%);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
.mar-r16{
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
255
src/components/headerNav.vue
Normal file
255
src/components/headerNav.vue
Normal file
@@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div class="headerNav navBg">
|
||||
<div style="position: relative">
|
||||
<ai-icon type="logo" :icon="system.logo||'iconcunwei'"/>
|
||||
<ai-icon type="logo" :icon="system.logo||'iconcunwei'" class="textShadow"/>
|
||||
</div>
|
||||
<span class="headerTitle">{{ system.fullTitle }}<div class="textShadow" v-html="system.fullTitle"/></span>
|
||||
<el-row type="flex" align="middle" class="toolbar">
|
||||
<!--下载中心-->
|
||||
<downloan-center-btn v-if="extra.downloadCenter"/>
|
||||
<!--大屏按钮-->
|
||||
<dv-btn v-if="extra.dv"/>
|
||||
<div class="btn" v-if="extra.showTool" @click="home.showTool=false">隐藏工具栏</div>
|
||||
<div class="btn" v-if="extra.helpDoc" @click="openHelp">帮助文档</div>
|
||||
<app-qrcode v-if="extra.appQRCode"/>
|
||||
<div class="btn" v-if="extra.customerService" @click.native="openAiService">
|
||||
<div class="iconfont iconCustomer_Service"></div>
|
||||
<div>智能客服</div>
|
||||
</div>
|
||||
<!--推荐链接-->
|
||||
<link-btn/>
|
||||
</el-row>
|
||||
<el-dropdown @visible-change="v=>isClick=v" @command="doMenu" class="rightDropdown">
|
||||
<el-row type="flex" align="middle">
|
||||
<el-avatar :src="user.info.avatar">
|
||||
{{ defaultAvatar }}
|
||||
</el-avatar>
|
||||
<span>{{ [user.info.name, user.info.roleName].join(" - ") }}</span>
|
||||
<i :class="dropdownIcon"/>
|
||||
</el-row>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">用户中心</el-dropdown-item>
|
||||
<el-dropdown-item command="signOut">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
import DvBtn from "./headerTools/dvBtn";
|
||||
import DownloanCenterBtn from "./headerTools/downloanCenterBtn";
|
||||
import extra from "../config.json"
|
||||
import AppQrcode from "./headerTools/appQrcode";
|
||||
import LinkBtn from "./headerTools/linkBtn";
|
||||
|
||||
export default {
|
||||
name: 'headerNav',
|
||||
components: {LinkBtn, AppQrcode, DownloanCenterBtn, DvBtn},
|
||||
data() {
|
||||
return {
|
||||
extra,
|
||||
isClick: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user', 'sys']),
|
||||
dropdownIcon() {
|
||||
return this.isClick ? 'el-icon-caret-top' : 'el-icon-caret-bottom'
|
||||
},
|
||||
defaultAvatar() {
|
||||
return this.user.info.name?.slice(-2) || "游客"
|
||||
},
|
||||
system: v => v.sys?.info || {}
|
||||
},
|
||||
created() {
|
||||
this.$dict.load('fileFrom');
|
||||
},
|
||||
methods: {
|
||||
// 获取最新的安卓、ios下载二维码
|
||||
doMenu(comm) {
|
||||
switch (comm) {
|
||||
case 'signOut':
|
||||
//登出
|
||||
this.$confirm("是否要登出?", {type: "warning"}).then(() => {
|
||||
this.$store.commit("signOut")
|
||||
}).catch(() => {
|
||||
})
|
||||
break;
|
||||
case 'user':
|
||||
this.$router.push({name: "个人中心"})
|
||||
break;
|
||||
}
|
||||
},
|
||||
openAiService() {
|
||||
window.open('http://v3.faqrobot.org/robot/chat1.html?sysNum=153543696570625098')
|
||||
},
|
||||
openHelp() {
|
||||
window.open('https://www.yuque.com/books/share/eeaaa5e3-a528-42eb-872e-20d661f3d0e2')
|
||||
},
|
||||
changeStatus(id) {
|
||||
this.$request.post(`/app/sysuserdownload/addOrUpdate`, {
|
||||
id, status: "2"
|
||||
}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.getFiles();
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.headerNav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 48px;
|
||||
position: fixed;
|
||||
z-index: 99;
|
||||
height: 48px;
|
||||
padding-left: 24px;
|
||||
box-sizing: border-box;
|
||||
top: 0;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
|
||||
.AiIcon {
|
||||
font-size: 38px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
background: linear-gradient(180deg, #FFFFFF 0%, #CCDBF6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.headerTitle {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 24px;
|
||||
color: #FFF;
|
||||
line-height: 28px;
|
||||
background: linear-gradient(180deg, #FFFFFF 0%, #CCDBF6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:deep(.toolbar) {
|
||||
gap: 12px;
|
||||
margin-right: 32px;
|
||||
|
||||
.btn {
|
||||
padding: 0 12px;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: rgba(#fff, .8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-dropdown {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
color: #fff;
|
||||
padding: 0 12px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(46, 51, 68, .15);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.el-image {
|
||||
margin: 12px 0 12px 16px;
|
||||
}
|
||||
|
||||
.download-wrapper {
|
||||
position: relative;
|
||||
|
||||
&:hover .download {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.download {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 12px;
|
||||
transform: translateX(-90%);
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.download-item {
|
||||
text-align: center;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 13px;
|
||||
}
|
||||
|
||||
& > img {
|
||||
width: 105px;
|
||||
height: 105px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 5px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.download-item__middle {
|
||||
img {
|
||||
width: 13px;
|
||||
height: 16px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
span {
|
||||
padding-left: 8px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.rightDropdown) {
|
||||
font-size: 12px;
|
||||
padding: 0 16px;
|
||||
height: 48px;
|
||||
background: rgba(#fff, .1);
|
||||
|
||||
.el-avatar > img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-row {
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
121
src/components/headerTools/appQrcode.vue
Normal file
121
src/components/headerTools/appQrcode.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<section class="appQrcode">
|
||||
<div class="btn">手机APP</div>
|
||||
<div class="download">
|
||||
<div class="download-item" v-if="!androidQRcode&&!iosQRcode"><p class="nowarp-text" v-text="`暂未发布`"/></div>
|
||||
<template v-else>
|
||||
<div class="download-item" v-if='iosQRcode'>
|
||||
<img :src="iosQRcode" alt=""/>
|
||||
<div class="download-item__middle">
|
||||
<span class="iconfont iconIOS"></span>
|
||||
<span>iPhone</span>
|
||||
</div>
|
||||
<p>手机扫码下载APP</p>
|
||||
</div>
|
||||
<div class="download-item" v-if='androidQRcode'>
|
||||
<img :src="androidQRcode" alt=""/>
|
||||
<div class="download-item__middle">
|
||||
<span class="iconfont iconAndroid"></span>
|
||||
<span>Android</span>
|
||||
</div>
|
||||
<p>手机扫码下载APP</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "appQrcode",
|
||||
data() {
|
||||
return {
|
||||
iosQRcode: null,
|
||||
androidQRcode: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAppQRcode() {
|
||||
this.$request.post(`/admin/sysversion/getLatestIosVersion`, null, {
|
||||
params: {type: 3}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.iosQRcode = res.data.qrCodeUrl
|
||||
}
|
||||
})
|
||||
|
||||
this.$request.post(`/admin/sysversion/getLatestVersion`, null, {
|
||||
params: {type: 1}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.androidQRcode = res.data.qrCodeUrl
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getAppQRcode()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.appQrcode {
|
||||
position: relative;
|
||||
|
||||
&:hover .download {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.download {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 12px;
|
||||
transform: translateX(-90%);
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.download-item {
|
||||
text-align: center;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 13px;
|
||||
}
|
||||
|
||||
& > img {
|
||||
width: 105px;
|
||||
height: 105px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 5px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.download-item__middle {
|
||||
img {
|
||||
width: 13px;
|
||||
height: 16px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
span {
|
||||
padding-left: 8px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
221
src/components/headerTools/downloanCenterBtn.vue
Normal file
221
src/components/headerTools/downloanCenterBtn.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<section class="downloanCenterBtn">
|
||||
<el-badge :value="badgeNum" :hidden='badgeNum==0'>
|
||||
<div class="btn" @click="openDrawer()">下载中心</div>
|
||||
</el-badge>
|
||||
<el-drawer title="下载中心" :visible.sync="drawer" :modal-append-to-body="false" size="520">
|
||||
<div class="downLoad_main">
|
||||
<div class="search_top ">
|
||||
<p style="color:#999999;">仅显示最近90天的记录</p>
|
||||
<el-input size="mini" v-model="fileName" placeholder="文件名" clearable prefix-icon="iconfont iconSearch"
|
||||
style="width:240px;" @change="getFiles()"/>
|
||||
</div>
|
||||
<ul class="infinite-list">
|
||||
<li v-for="(item,i) in filesList" class="infinite-list-item " :key="i">
|
||||
<div class="left">
|
||||
<svg class="svg" aria-hidden="true">
|
||||
<use xlink:href="#iconZip"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="middle">
|
||||
<p class="fileName">{{ item.fileName }}【密码:{{ item.pwd }}】</p>
|
||||
<p>
|
||||
<span>来源:</span>
|
||||
<span>{{ $dict.getLabel('fileFrom', item.fileFrom) }}</span>
|
||||
<span>{{ (item.size / 1000).toFixed(2) + "KB" }}</span>
|
||||
<span>{{ item.createTime }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="right">
|
||||
<span class="iconfont iconResetting" v-if="item.status==0">处理中</span>
|
||||
<span v-if="item.status==2">已下载</span>
|
||||
<i class="iconfont iconDownload" @click="downFile(item)" v-if="item.status!=0">下载</i>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "downloanCenterBtn",
|
||||
data() {
|
||||
return {
|
||||
badgeNum: 0,
|
||||
drawer: false,//抽屉
|
||||
filesList: [],
|
||||
fileName: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user'])
|
||||
},
|
||||
methods: {
|
||||
openDrawer() {
|
||||
this.drawer = true;
|
||||
this.getFiles();
|
||||
},
|
||||
getFiles() {
|
||||
this.$request.post(`/app/sysuserdownload/list`, null, {
|
||||
params: {
|
||||
userId: this.user.info.id,
|
||||
fileName: this.fileName,
|
||||
current: 1,
|
||||
size: 1000,
|
||||
}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.filesList = res.data.records;
|
||||
this.searchNum()
|
||||
}
|
||||
});
|
||||
},
|
||||
//查询未完成数量
|
||||
searchNum() {
|
||||
this.$request.post(`/app/sysuserdownload/queryCountByUserId`, null, {
|
||||
params: {
|
||||
userId: this.user.info.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.badgeNum = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
downFile(item) {
|
||||
this.changeStatus(item.id);
|
||||
// window.open(item.accessUrl);
|
||||
let elemIF = document.createElement('iframe');
|
||||
elemIF.src = item.accessUrl;
|
||||
elemIF.style.display = 'none';
|
||||
document.body.appendChild(elemIF);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.searchNum()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.downloanCenterBtn {
|
||||
.downLoad_main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.search_top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.infinite-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.infinite-list-item {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(208, 212, 220, 1);
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
|
||||
.svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.middle {
|
||||
flex: 1;
|
||||
|
||||
.fileName {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
p:nth-child(2) {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
|
||||
span {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
span:nth-child(2) {
|
||||
border-right: solid 1px #999999;
|
||||
}
|
||||
|
||||
span:nth-child(3) {
|
||||
border-right: solid 1px #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
width: 90px;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
i {
|
||||
display: block;
|
||||
width: 50px;
|
||||
color: #5088FF;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #8888;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-drawer__wrapper) {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
.el-drawer__header > span:focus {
|
||||
outline: 0
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
src/components/headerTools/dvBtn.vue
Normal file
52
src/components/headerTools/dvBtn.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<section class="dvBtn">
|
||||
<el-popover title="数据大屏" width="500" trigger="click">
|
||||
<div flex class="wrap">
|
||||
<div class="el-button--text pad-r8 pad-b8" style="width: 50%" v-for="op in dvOptions" :key="op.id" v-text="op.name||'无名大屏'"
|
||||
@click="handleOpenDV(op.id)"/>
|
||||
</div>
|
||||
<div slot="reference" class="btn">数据大屏</div>
|
||||
</el-popover>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import extra from "../../config.json"
|
||||
|
||||
export default {
|
||||
name: "dvBtn",
|
||||
data() {
|
||||
return {
|
||||
dvOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDvList() {
|
||||
this.$request.post("/app/appdiylargescreen/allLargeScreenProjectByPage", null, {
|
||||
params: {size: 9999, status: 1}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.dvOptions = res.data.records
|
||||
}
|
||||
})
|
||||
},
|
||||
handleOpenDV(id) {
|
||||
window.open(`${location.origin}${extra.base || ""}/dv?id=${id}#dv`)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getDvList()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dvBtn {
|
||||
|
||||
}
|
||||
|
||||
:deep(.el-button--text) {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
46
src/components/headerTools/linkBtn.vue
Normal file
46
src/components/headerTools/linkBtn.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<section class="linkBtn">
|
||||
<el-dropdown v-if="links.length > 0" @command="handleOpenLink">
|
||||
<div class="btn">友情链接</div>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="op in links" :key="op.id" :command="op.url">
|
||||
{{ op.title }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "linkBtn",
|
||||
label: "友情链接",
|
||||
data() {
|
||||
return {
|
||||
links: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getLinks() {
|
||||
this.$request.post("/app/appwebnavurl/list", null, {
|
||||
params: {size: 9999, status: 1}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.links = res.data.records
|
||||
}
|
||||
})
|
||||
},
|
||||
handleOpenLink(url) {
|
||||
window.open(url)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getLinks()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.linkBtn {
|
||||
}
|
||||
</style>
|
||||
40
src/components/mainContent.vue
Normal file
40
src/components/mainContent.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<section class="mainContent">
|
||||
<ai-nav-tab :fixed="homePage" :routes="routes"/>
|
||||
<router-view v-if="refresh"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "mainContent",
|
||||
computed: {
|
||||
...mapState(['user', 'homePage']),
|
||||
routes: v => v.user.info?.menuSet?.map(e => ({...e, label: e.name, name: e.id}))
|
||||
},
|
||||
watch: {
|
||||
$route(v, old) {
|
||||
if (v.meta == old.meta && v.fullPath != old.fullPath) {
|
||||
this.refresh = false
|
||||
this.$nextTick(() => this.refresh = true)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
refresh: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mainContent {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
296
src/components/sliderNav.vue
Normal file
296
src/components/sliderNav.vue
Normal file
@@ -0,0 +1,296 @@
|
||||
<template>
|
||||
<section class="sliderNav">
|
||||
<el-input class="searchApp" size="small" v-model="searchApp" placeholder="搜索应用" clearable
|
||||
prefix-icon="iconfont iconSearch" @change="handleSearchApp"/>
|
||||
<el-scrollbar class="ai-menu">
|
||||
<div v-for="(item,i) in navs" :key="i">
|
||||
<div class="rootMenu" :class="{isActive:menuPath.includes(item.id)}" @click.stop="openKidMenu(item)">
|
||||
<i :class="item.style||'iconfont iconloudongmoxing'"/>
|
||||
<span class="fill mar-l8" v-text="item.name"/>
|
||||
<i v-if="hasChildren(item.children)" class="iconfont mar-l8" :class="arrowIcon(item.showChildren)"/>
|
||||
</div>
|
||||
<div class="kidMenu" v-if="hasChildren(item.children)&&item.showChildren" @click.stop>
|
||||
<div v-for="menu in item.children" :key="menu.id">
|
||||
<div class="submenu wrap pad-l16 pad-r16" flex v-if="hasChildren(menu.children)">
|
||||
<b v-text="menu.name" :class="{menuBtn:menu.type==1,current:menuPath.includes(menu.id)}"
|
||||
@click="handleSelect(menu)"/>
|
||||
<div class="menuBtn" v-for="kid in menu.children" :key="kid.id" v-text="kid.name" :title="kid.name"
|
||||
@click="handleSelect(kid)" :class="{current:menuPath.includes(kid.id)}"/>
|
||||
</div>
|
||||
<div v-else class="lv2Btn" v-text="menu.name" @click="handleSelect(menu)" :class="{current:menuPath.includes(menu.id)}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"/>
|
||||
</el-scrollbar>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from "vuex";
|
||||
import qs from "querystring";
|
||||
|
||||
export default {
|
||||
name: "sliderNav",
|
||||
data() {
|
||||
return {
|
||||
menuList: [],
|
||||
searchApp: "",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['mods']),
|
||||
navs: v => v.sortList(v.menuList),
|
||||
menuPath() {
|
||||
let paths = [], current = this.mods?.find(e => e.route == this.$route.name)
|
||||
const findParent = id => {
|
||||
let menu = this.mods?.find(e => e.id == id)
|
||||
if (menu) {
|
||||
paths.push(menu.id)
|
||||
if (!!menu.parentId) findParent(menu.parentId)
|
||||
}
|
||||
}
|
||||
if (current) {
|
||||
findParent(current.id)
|
||||
}
|
||||
return paths
|
||||
},
|
||||
modList: v => v.mods.filter(e => e.isMenu == 1 || e.type == 0 || (e.level > 1 && e.type == 1))
|
||||
},
|
||||
methods: {
|
||||
initMenu(menus = this.modList) {
|
||||
//isMenu 旧版本判断是否为菜单 type<2 新版本判断是否是菜单或应用
|
||||
if (menus?.length > 0) {
|
||||
this.menuList = this.$arr2tree(menus)
|
||||
this.menuList = this.menuList.map(e => ({
|
||||
...e,
|
||||
showChildren: this.menuPath.includes(e.id) || !!this.searchApp
|
||||
}))
|
||||
}
|
||||
},
|
||||
openKidMenu(parent) {
|
||||
if (this.hasChildren(parent.children)) {
|
||||
parent.showChildren = !parent.showChildren
|
||||
} else {
|
||||
this.handleSelect(parent)
|
||||
}
|
||||
},
|
||||
handleSelect(item) {
|
||||
if (!item.path) return
|
||||
if (item.route == this.$route.name) {
|
||||
//避免同一路由跳转的BUG vue-router官方BUG
|
||||
} else {
|
||||
let {route: name, path} = item
|
||||
if (!name) {
|
||||
this.$message.warning("暂无应用")
|
||||
} else {
|
||||
this.goto({name, query: qs.parse(path.split("?")?.[1])})
|
||||
}
|
||||
}
|
||||
},
|
||||
goto(item) {
|
||||
return this.$router.push(item)
|
||||
},
|
||||
sortList(list) {
|
||||
return list?.sort((a, b) => a.showIndex - b.showIndex) || []
|
||||
},
|
||||
handleSearchApp() {
|
||||
let {searchApp} = this
|
||||
if (searchApp) {
|
||||
let list = this.modList.filter(e => e.name?.indexOf(searchApp) > -1), map = {}
|
||||
const findParent = e => {
|
||||
map[e.id] = e
|
||||
if (!!e.parentId) {
|
||||
let parent = this.modList.find(m => m.id == e.parentId)
|
||||
map[parent.id] = parent
|
||||
if (!!parent.parentId) {
|
||||
findParent(parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
list.forEach(e => findParent(e))
|
||||
console.log(map, list)
|
||||
this.initMenu(Object.values(map))
|
||||
} else {
|
||||
this.initMenu()
|
||||
}
|
||||
},
|
||||
arrowIcon(v) {
|
||||
return v ? "iconArrow_Down" : "iconArrow_Right"
|
||||
},
|
||||
hasChildren(arr) {
|
||||
return arr?.length > 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initMenu()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.sliderNav {
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
transition: width .1s;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #e5e5e5;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
background: #EFF1F4;
|
||||
color: #222;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
.kidMenu {
|
||||
font-size: 13px;
|
||||
|
||||
.rootName {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.submenu {
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
color: #aaa;
|
||||
|
||||
& > b {
|
||||
width: 100%;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
& > * {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.menuBtn {
|
||||
display: block;
|
||||
width: 50%;
|
||||
cursor: pointer;
|
||||
line-height: 32px;
|
||||
color: #333;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&.line {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #26f;
|
||||
}
|
||||
|
||||
&.current {
|
||||
color: #26f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rootMenu {
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px -1px 0px 0px #D8DCE3 inset, 0px 1px 0px 0px #FFF inset, -1px 0px 0px 0px #E5E5E5 inset;
|
||||
font-size: 13px;
|
||||
|
||||
.iconfont {
|
||||
color: #89B;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
color: #26f;
|
||||
|
||||
.iconfont {
|
||||
color: #26f !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #26f;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ai-menu ){
|
||||
padding-left: 0;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
|
||||
.el-scrollbar__wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.searchApp ){
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
padding: 0 16px;
|
||||
box-shadow: 0px -1px 0px 0px #E5E5E5 inset;
|
||||
|
||||
.el-input__inner {
|
||||
border: none;
|
||||
background: inherit;
|
||||
padding: 0 28px;
|
||||
}
|
||||
|
||||
.el-input__prefix {
|
||||
left: 16px;
|
||||
|
||||
.iconSearch {
|
||||
font-size: 20px;
|
||||
width: fit-content;
|
||||
color: #89B;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
color: #aaa;
|
||||
border-top: 1px solid #ddd;
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
margin: 16px 16px 32px;
|
||||
|
||||
&:before {
|
||||
content: "到达底部";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 0 16px;
|
||||
background: #EFF1F4;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.lv2Btn {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #222;
|
||||
padding-left: 44px;
|
||||
cursor: pointer;
|
||||
|
||||
&.current {
|
||||
background: linear-gradient(90deg, #298BFF 0%, #0C61FF 100%);
|
||||
box-shadow: inset -1px 0 0 0 #E5E5E5, inset 0 2px 8px 0 #1E4599;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user