用户信息修改

This commit is contained in:
仲么了
2024-01-26 16:15:40 +08:00
parent 04665478fc
commit 5267ae1903
13 changed files with 457 additions and 173 deletions

View File

@@ -1,18 +1,15 @@
# Vue 3 + TypeScript + Vite Vue 3 + TypeScript + Vite
这个模板可以帮助您开始使用Vue 3和TypeScript在Vite中进行开发。该模板使用了Vue 3的<script setup>script setup
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. 推荐的IDE设置
VS Code + Volar并禁用Vetur+ TypeScript Vue Plugin (Volar)。
在TS中对.vue导入提供类型支持
TypeScript 默认情况下无法处理.vue导入的类型信息因此我们用vue-tsc替换了tsc CLI进行类型检查。在编辑器中我们需要TypeScript Vue Plugin (Volar)来让TypeScript语言服务了解.vue类型。
## Recommended IDE Setup 如果独立的TypeScript插件对您来说不够快Volar还实现了一种Take Over Mode它的性能更好。您可以按照以下步骤启用它
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 禁用内置的TypeScript扩展
从VSCode的命令面板中运行Extensions: Show Built-in Extensions
找到TypeScript and JavaScript Language Features右键单击并选择Disable (Workspace)
通过运行Developer: Reload Window命令重新加载VSCode窗口。
## Type Support For `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.

View File

@@ -34,13 +34,22 @@ export function login(params: LoginData) {
}) })
} }
//获取用户信息
export function getUserById() {
const adminInfo = useAdminInfo()
return request({
url: '/user-boot/user/getUserById?id=' + adminInfo.userIndex,
method: 'get'
})
}
// 刷新token // 刷新token
export function refreshToken(): Promise<any> { export function refreshToken(): Promise<any> {
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
return login({ return login({
grant_type: 'refresh_token', grant_type: 'refresh_token',
refresh_token: adminInfo.refresh_token, refresh_token: adminInfo.refresh_token,
username: adminInfo.username username: adminInfo.loginName
}) })
} }
@@ -108,8 +117,7 @@ export async function updatePassword(params: any) {
} }
export function checkUser(data: any) {
export function checkUser(data:any) {
return request({ return request({
url: '/user-boot/user/check', url: '/user-boot/user/check',
method: 'put', method: 'put',

View File

@@ -24,40 +24,21 @@
size="18" size="18"
/> />
</div> </div>
<el-popover <el-dropdown style='height: 100%;' @command='handleCommand'>
@show="onCurrentNavMenu(true, 'adminInfo')" <div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''">
@hide="onCurrentNavMenu(false, 'adminInfo')" <el-avatar :size="25" fit="fill">
placement="bottom-end" <img src="@/assets/avatar.png" alt="" />
:hide-after="0" </el-avatar>
:width="260" <div class="admin-name">{{ adminInfo.nickname }}</div>
trigger="click"
popper-class="admin-info-box"
v-model:visible="state.showAdminInfoPopover"
>
<template #reference>
<div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''">
<el-avatar :size="25" fit="fill">
<img src="@/assets/avatar.png" alt="" />
</el-avatar>
<div class="admin-name">{{ adminInfo.nickname }}</div>
</div>
</template>
<div>
<div class="admin-info-base">
<el-avatar :size="70" fit="fill">
<img src="@/assets/avatar.png" alt="" />
</el-avatar>
<div class="admin-info-other">
<div class="admin-info-name">{{ adminInfo.nickname }}</div>
<div class="admin-info-lasttime">{{ adminInfo.last_login_time }}</div>
</div>
</div>
<div class="admin-info-footer">
<el-button @click="onAdminInfo" type="primary" plain>个人资料</el-button>
<el-button @click="onLogout" type="danger" plain>退出登录</el-button>
</div>
</div> </div>
</el-popover> <template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="adminInfo">个人资料</el-dropdown-item>
<el-dropdown-item command="changePwd">修改密码</el-dropdown-item>
<el-dropdown-item command="layout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div @click="configStore.setLayout('showDrawer', true)" class="nav-menu-item"> <div @click="configStore.setLayout('showDrawer', true)" class="nav-menu-item">
<Icon <Icon
:color="configStore.getColorVal('headerBarTabColor')" :color="configStore.getColorVal('headerBarTabColor')"
@@ -67,27 +48,30 @@
/> />
</div> </div>
<Config /> <Config />
<PopupPwd ref='popupPwd' />
<AdminInfo ref='popupAdminInfo' />
<!-- <TerminalVue /> --> <!-- <TerminalVue /> -->
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive } from 'vue' import { reactive, ref } from 'vue'
import screenfull from 'screenfull' import screenfull from 'screenfull'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import Config from './config.vue' import Config from './config.vue'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { Local, Session } from '@/utils/storage'
import { ADMIN_INFO } from '@/stores/constant/cacheKey'
import router from '@/router' import router from '@/router'
import { routePush } from '@/utils/router' import { routePush } from '@/utils/router'
import { fullUrl } from '@/utils/common' import { fullUrl } from '@/utils/common'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import PopupPwd from './popup/password.vue'
import AdminInfo from './popup/adminInfo.vue'
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
const configStore = useConfig() const configStore = useConfig()
const popupPwd = ref()
const popupAdminInfo = ref()
const state = reactive({ const state = reactive({
isFullScreen: false, isFullScreen: false,
currentNavMenu: '', currentNavMenu: '',
@@ -99,10 +83,9 @@ const onCurrentNavMenu = (status: boolean, name: string) => {
state.currentNavMenu = status ? name : '' state.currentNavMenu = status ? name : ''
} }
const savePng = () => { const savePng = () => {
html2canvas(document.body,{ html2canvas(document.body, {
scale: 1, scale: 1,
useCORS: true useCORS: true
}).then(function (canvas) { }).then(function (canvas) {
var link = document.createElement('a') var link = document.createElement('a')
link.href = canvas.toDataURL('image/png') link.href = canvas.toDataURL('image/png')
@@ -126,19 +109,23 @@ const onAdminInfo = () => {
routePush({ name: 'routine/adminInfo' }) routePush({ name: 'routine/adminInfo' })
} }
const onLogout = () => { const handleCommand = (key: string) => {
router.push({ name: 'login' }) console.log(key)
switch (key) {
case 'adminInfo':
popupAdminInfo.value.open()
break
case 'changePwd':
popupPwd.value.open()
break
case 'layout':
router.push({ name: 'login' })
break
default:
break
}
} }
// const onClearCache = (type: string) => {
// if (type == 'storage' || type == 'all') {
// const adminInfo = Local.get(ADMIN_INFO)
// Session.clear()
// Local.clear()
// Local.set(ADMIN_INFO, adminInfo)
// if (type == 'storage') return
// }
// }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -146,12 +133,14 @@ const onLogout = () => {
border-radius: var(--el-border-radius-base); border-radius: var(--el-border-radius-base);
box-shadow: var(--el-box-shadow-light); box-shadow: var(--el-box-shadow-light);
} }
.nav-menus { .nav-menus {
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%; height: 100%;
margin-left: auto; margin-left: auto;
background-color: v-bind('configStore.getColorVal("headerBarBackground")'); background-color: v-bind('configStore.getColorVal("headerBarBackground")');
.nav-menu-item { .nav-menu-item {
height: 100%; height: 100%;
width: 40px; width: 40px;
@@ -159,16 +148,19 @@ const onLogout = () => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
.nav-menu-icon { .nav-menu-icon {
box-sizing: content-box; box-sizing: content-box;
color: v-bind('configStore.getColorVal("headerBarTabColor")'); color: v-bind('configStore.getColorVal("headerBarTabColor")');
} }
&:hover { &:hover {
.icon { .icon {
animation: twinkle 0.3s ease-in-out; animation: twinkle 0.3s ease-in-out;
} }
} }
} }
.admin-info { .admin-info {
display: flex; display: flex;
height: 100%; height: 100%;
@@ -177,48 +169,58 @@ const onLogout = () => {
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
color: v-bind('configStore.getColorVal("headerBarTabColor")'); color: v-bind('configStore.getColorVal("headerBarTabColor")');
&:hover { &:hover {
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")'); color: v-bind('configStore.getColorVal("headerBarTabActiveColor")');
} }
} }
.admin-name { .admin-name {
padding-left: 6px; padding-left: 6px;
white-space: nowrap; white-space: nowrap;
} }
.nav-menu-item:hover, .nav-menu-item:hover,
.admin-info:hover, .admin-info:hover,
.nav-menu-item.hover, .nav-menu-item.hover,
.admin-info.hover { .admin-info.hover {
background: v-bind('configStore.getColorVal("headerBarHoverBackground")'); background: v-bind('configStore.getColorVal("headerBarHoverBackground")');
.nav-menu-icon { .nav-menu-icon {
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")') !important; color: v-bind('configStore.getColorVal("headerBarTabActiveColor")') !important;
} }
} }
} }
.dropdown-menu-box :deep(.el-dropdown-menu__item) { .dropdown-menu-box :deep(.el-dropdown-menu__item) {
justify-content: center; justify-content: center;
} }
.admin-info-base { .admin-info-base {
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
padding-top: 10px; padding-top: 10px;
.admin-info-other { .admin-info-other {
display: block; display: block;
width: 100%; width: 100%;
text-align: center; text-align: center;
padding: 10px 0; padding: 10px 0;
.admin-info-name { .admin-info-name {
font-size: var(--el-font-size-large); font-size: var(--el-font-size-large);
} }
} }
} }
.admin-info-footer { .admin-info-footer {
padding: 10px 0; padding: 10px 0;
margin: 0 -12px -12px -12px; margin: 0 -12px -12px -12px;
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
} }
.pt2 { .pt2 {
padding-top: 2px; padding-top: 2px;
} }

View File

@@ -0,0 +1,54 @@
<template>
<el-dialog class='cn-operate-dialog' v-model='dialogVisible' :title='title'>
<el-scrollbar>
<el-form :inline='false' :model='form' label-width='120px'>
<el-form-item label='用户名称:'>
<el-input v-model='form.name' :disabled='true'></el-input>
</el-form-item>
<el-form-item label='登录名称:' class='top'>
<el-input v-model='form.loginName' :disabled='true'></el-input>
</el-form-item>
<el-form-item label='归属部门名称:' class='top'>
<el-input v-model='form.deptName' :disabled='true'></el-input>
</el-form-item>
<el-form-item label='拥有的角色:' class='top'>
<el-input v-model='form.role' :disabled='true'></el-input>
</el-form-item>
<el-form-item label='电话号码:' class='top'>
<el-input v-model='form.phone' :disabled='true'></el-input>
</el-form-item>
<el-form-item label='电子邮箱:' class='top'>
<el-input v-model='form.email' :disabled='true'></el-input>
</el-form-item>
</el-form>
</el-scrollbar>
</el-dialog>
</template>
<script lang='ts' setup>
import { ref, inject } from 'vue'
import { reactive } from 'vue'
import { useAdminInfo } from '@/stores/adminInfo'
const dialogVisible = ref(false)
const title = ref('用户信息')
const adminInfo = useAdminInfo()
const formRef = ref()
const form = reactive({
name: '',
deptName: '',
phone: '',
email: '',
role: '',
loginName: ''
})
const open = () => {
dialogVisible.value = true
for (const key in form) {
form[key] = Array.isArray(adminInfo.$state[key]) ? adminInfo.$state[key].join(',') : adminInfo.$state[key]
}
}
defineExpose({ open })
</script>

View File

@@ -0,0 +1,107 @@
<template>
<el-dialog class='cn-operate-dialog' v-model='dialogVisible' :title='title'>
<el-scrollbar>
<el-form :inline='false' :model='form' label-width='120px' :rules='rules' ref='formRef'>
<el-form-item label='校验密码:' prop='password'>
<el-input v-model='form.password' type='password' placeholder='请输入校验密码' show-password />
</el-form-item>
<el-form-item label='新密码:' prop='newPwd' style='margin-top: 20px'>
<el-input v-model='form.newPwd' type='password' placeholder='请输入新密码' show-password />
</el-form-item>
<el-form-item label='确认密码:' prop='confirmPwd' style='margin-top: 20px'>
<el-input v-model='form.confirmPwd' type='password' placeholder='请输入确认密码' show-password />
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<span class='dialog-footer'>
<el-button @click='dialogVisible = false'>取消</el-button>
<el-button type='primary' @click='submit'>确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang='ts' setup>
import { ref, inject } from 'vue'
import { reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { passwordConfirm, updatePassword } from '@/api/user-boot/user'
import { validatePwd } from '@/utils/common'
import { useAdminInfo } from '@/stores/adminInfo'
const adminInfo = useAdminInfo()
const dialogVisible = ref(false)
const title = ref('修改密码')
const formRef = ref()
// 注意不要和表单ref的命名冲突
const form = reactive({
password: '',
newPwd: '',
confirmPwd: ''
})
const rules = {
password: [
{ required: true, message: '请输入校验密码', trigger: 'blur' },
{
min: 6,
max: 16,
message: '长度在 6 到 16 个字符',
trigger: 'blur'
}
],
newPwd: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{
min: 6,
max: 16,
message: '长度在 6 到 16 个字符',
trigger: 'blur'
},
{ validator: validatePwd, trigger: 'blur' }
],
confirmPwd: [
{ required: true, message: '请确认密码', trigger: 'blur' },
{
min: 6,
max: 16,
message: '长度在 6 到 16 个字符',
trigger: 'blur'
},
{
validator: (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('请再次输入密码'))
} else if (value !== form.newPwd) {
callback(new Error('两次输入密码不一致!'))
} else {
callback()
}
},
trigger: 'blur',
required: true
}
]
}
const open = () => {
dialogVisible.value = true
}
const submit = () => {
formRef.value.validate(async valid => {
if (valid) {
passwordConfirm(form.password)
.then(res => {
updatePassword({
id: adminInfo.$state.userIndex,
newPassword: form.newPwd
}).then((res: any) => {
ElMessage.success('密码修改成功')
dialogVisible.value = false
})
})
}
})
}
defineExpose({ open })
</script>

View File

@@ -23,6 +23,7 @@ import { adminBaseRoutePath } from '@/router/static'
import { getRouteMenu, dictDataCache } from '@/api/auth' import { getRouteMenu, dictDataCache } from '@/api/auth'
import { getAreaList } from '@/api/common' import { getAreaList } from '@/api/common'
import { BasicDictData } from '@/stores/interface' import { BasicDictData } from '@/stores/interface'
import { getUserById } from '@/api/user-boot/user'
defineOptions({ defineOptions({
components: { Default, Classic, Streamline, Double } components: { Default, Classic, Streamline, Double }
@@ -50,9 +51,10 @@ onBeforeMount(() => {
}) })
const init = async () => { const init = async () => {
await Promise.all([getAreaList(), dictDataCache()]).then(res => { await Promise.all([getAreaList(), dictDataCache(),getUserById()]).then(res => {
dictData.state.area = res[0].data dictData.state.area = res[0].data
dictData.state.basic = res[1].data dictData.state.basic = res[1].data
adminInfo.dataFill(res[2].data)
}) })
/** /**
* 后台初始化请求,获取站点配置,动态路由等信息 * 后台初始化请求,获取站点配置,动态路由等信息

View File

@@ -11,13 +11,36 @@ export const useAdminInfo = defineStore('adminInfo', {
expires_in: 0, expires_in: 0,
scope: '', scope: '',
nickname: '', nickname: '',
userType: 1, userType: 0,
deptIndex: '', deptIndex: '',
userIndex: '', userIndex: '',
client_id: '', client_id: '',
headSculpture: '', headSculpture: '',
jti: '', jti: '',
loginName: '' name: '',
deptId: '',
phone: '',
email: '',
limitIpStart: '',
limitIpEnd: '',
limitTime: '',
casualUser: 0,
type: 0,
smsNotice: 0,
emailNotice: 0,
role: [],
devCode: '',
id: '',
loginName: '',
state: 0,
registerTime: '',
loginTime: '',
deptName: '',
areaId: '',
areaName: '',
deptLevel: 0,
roleList: [],
roleCode: []
} }
}, },
actions: { actions: {

View File

@@ -45,8 +45,32 @@ export interface AdminInfo {
deptIndex: string deptIndex: string
userIndex: string userIndex: string
client_id: string client_id: string
headSculpture: string headSculpture: any
jti: string jti: string
name: string
deptId: string
phone: string
email: string
limitIpStart: string
limitIpEnd: string
limitTime: string
casualUser: number
type: number
smsNotice: number
emailNotice: number
role: string[]
devCode: any
id: string
loginName: string
state: number
registerTime: string
loginTime: string
deptName: string
areaId: string
areaName: string
deptLevel: number
roleList: string[]
roleCode: string[]
} }
export interface DictData { export interface DictData {

1
src/template/README.md Normal file
View File

@@ -0,0 +1 @@
dialog 弹框标准模版

78
src/template/dialog.vue Normal file
View File

@@ -0,0 +1,78 @@
<template>
<el-dialog class='cn-operate-dialog' v-model='dialogVisible' :title='title'>
<el-scrollbar>
<el-form :inline='false' :model='form' label-width='120px' :rules='rules' ref='formRef'>
<el-form-item label='角色名称'>
<el-input v-model='form.name' placeholder='请输入菜单名称' />
</el-form-item>
<el-form-item label='角色编码'>
<el-input v-model='form.code' placeholder='请输入菜单名称' />
</el-form-item>
<el-form-item label='角色描述'>
<el-input v-model='form.remark' :rows='2' type='textarea' placeholder='请输入描述' />
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<span class='dialog-footer'>
<el-button @click='dialogVisible = false'>取消</el-button>
<el-button type='primary' @click='submit'>确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang='ts' setup>
import { ref, inject } from 'vue'
import { reactive } from 'vue'
import { ElMessage } from 'element-plus'
import TableStore from '@/utils/tableStore' // 若不是列表页面弹框可删除
const dialogVisible = ref(false)
const title = ref('')
const tableStore = inject('tableStore') as TableStore
const formRef = ref()
// 注意不要和表单ref的命名冲突
const form = reactive({
code: '',
name: '',
remark: '',
id: ''
})
const rules = {
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
code: [{ required: true, message: '角色编码不能为空', trigger: 'blur' }]
}
const open = (text: string, data?: anyObj) => {
title.value = text
dialogVisible.value = true
if (data) {
// 表单赋值
for (let key in form) {
form[key] = data[key]
}
} else {
// 在此处恢复默认表单
for (let key in form) {
form[key] = ''
}
}
}
const submit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
if (form.id) {
// await update(form)
} else {
// await create(form)
}
ElMessage.success('保存成功')
tableStore.index()
dialogVisible.value = false
}
})
}
defineExpose({ open })
</script>

View File

@@ -47,22 +47,6 @@ const rules = {
message: '长度在 6 到 16 个字符', message: '长度在 6 到 16 个字符',
trigger: 'blur' trigger: 'blur'
}, },
{
validator: (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('请输入旧密码'))
} else {
passwordConfirm(value)
.then(res => {
callback()
})
.catch(() => {
callback(new Error('旧密码不正确'))
})
}
},
trigger: 'blur'
}
], ],
newPwd: [ newPwd: [
{ required: true, message: '请输入密码', trigger: 'blur' }, { required: true, message: '请输入密码', trigger: 'blur' },
@@ -109,12 +93,14 @@ const open = (id: string) => {
const submit = async () => { const submit = async () => {
formRef.value.validate((valid:boolean) => { formRef.value.validate((valid:boolean) => {
if (valid) { if (valid) {
updatePassword({ passwordConfirm(form.password).then((res: any) => {
id: form.id, updatePassword({
newPassword: form.newPwd id: form.id,
}).then((res: any) => { newPassword: form.newPwd
ElMessage.success('密码修改成功') }).then((res: any) => {
dialogVisible.value = false ElMessage.success('密码修改成功')
dialogVisible.value = false
})
}) })
} }
}) })

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="default-main device-manage" :style="{ height: pageHeight.height }" v-loading="loading"> <div class="default-main device-manage" :style="{ height: pageHeight.height }" v-loading="loading">
<DeviceTree @node-click="nodeClick" @init="nodeClick"></DeviceTree> <DeviceTree @node-click="nodeClick" @init="nodeClick"></DeviceTree>
<div class="device-manage-right" v-if="deviceData" > <div class="device-manage-right" v-if="deviceData">
<el-descriptions title="设备基本信息" class="mb10" :column="3" border> <el-descriptions title="设备基本信息" class="mb10" :column="3" border>
<template #extra> <template #extra>
<el-button <el-button
@@ -39,7 +39,7 @@
lazy lazy
:label="item.name" :label="item.name"
:name="item.id" :name="item.id"
v-for="(item,index) in deviceData.dataSetList" v-for="(item, index) in deviceData.dataSetList"
:key="index" :key="index"
></el-tab-pane> ></el-tab-pane>
<div :style="{ height: tableHeight }" v-loading="tableLoading"> <div :style="{ height: tableHeight }" v-loading="tableLoading">
@@ -131,7 +131,7 @@ queryByCode('Device_Type').then(res => {
}) })
}) })
const echoName = (value: any, arr: any[]) => { const echoName = (value: any, arr: any[]) => {
return arr.find(item => item.value == value).label return arr.find(item => item.value == value)?.label
} }
const openGroup = () => { const openGroup = () => {
if (!dataSet.value) { if (!dataSet.value) {

View File

@@ -1,136 +1,136 @@
<template> <template>
<div class="default-main"> <div class='default-main'>
<TableHeader> <TableHeader>
<template v-slot:select> <template v-slot:select>
<el-form-item label="设备类型"> <el-form-item label='设备类型'>
<!-- <el-input v-model="tableStore.table.params.searchValue" placeholder="请输入设备类型" /> --> <!-- <el-input v-model="tableStore.table.params.searchValue" placeholder="请输入设备类型" /> -->
<el-select <el-select
v-model="tableStore.table.params.devType" v-model='tableStore.table.params.devType'
clearable clearable
@change="devTypeChange" @change='devTypeChange'
placeholder="请选择设备类型" placeholder='请选择设备类型'
> >
<el-option <el-option
v-for="item in devTypeOptions" v-for='item in devTypeOptions'
:key="item.value" :key='item.value'
:label="item.label" :label='item.label'
:value="item.value" :value='item.value'
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="设备型号"> <el-form-item label='设备型号'>
<el-select v-model="tableStore.table.params.devModel" clearable placeholder="请选择设备型号"> <el-select v-model='tableStore.table.params.devModel' clearable placeholder='请选择设备型号'>
<el-option <el-option
v-for="item in devModelOptionsFilter" v-for='item in devModelOptionsFilter'
:key="item.value" :key='item.value'
:label="item.label" :label='item.label'
:value="item.value" :value='item.value'
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="通讯协议"> <el-form-item label='通讯协议'>
<el-select v-model="tableStore.table.params.devAccessMethod" clearable placeholder="请选择通讯协议"> <el-select v-model='tableStore.table.params.devAccessMethod' clearable placeholder='请选择通讯协议'>
<el-option label="MQTT" value="mqtt"></el-option> <el-option label='MQTT' value='mqtt'></el-option>
<el-option label="CLD" value="cloud"></el-option> <el-option label='CLD' value='cloud'></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="状态"> <el-form-item label='状态'>
<el-select v-model="tableStore.table.params.status" clearable placeholder="请选择状态"> <el-select v-model='tableStore.table.params.status' clearable placeholder='请选择状态'>
<el-option label="未注册" :value="1"></el-option> <el-option label='未注册' :value='1'></el-option>
<el-option label="注册" :value="2"></el-option> <el-option label='注册' :value='2'></el-option>
<el-option label="接入" :value="3"></el-option> <el-option label='接入' :value='3'></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</template> </template>
<template v-slot:operation> <template v-slot:operation>
<el-button type="primary" @click="downLoadFile" class="ml10" icon="el-icon-Download"> <el-button type='primary' @click='downLoadFile' class='ml10' icon='el-icon-Download'>
模版下载 模版下载
</el-button> </el-button>
<el-upload <el-upload
style="display: inline-block" style='display: inline-block'
action="" action=''
accept=".xlsx" accept='.xlsx'
class="upload-demo" class='upload-demo'
:show-file-list="false" :show-file-list='false'
:auto-upload="false" :auto-upload='false'
:on-change="bulkImport" :on-change='bulkImport'
> >
<el-button type="primary" class="ml10" icon="el-icon-Tickets">批量导入</el-button> <el-button type='primary' class='ml10' icon='el-icon-Tickets'>批量导入</el-button>
</el-upload> </el-upload>
<el-button type="primary" class="ml10" @click="add" icon="el-icon-Plus">新增设备</el-button> <el-button type='primary' class='ml10' @click='add' icon='el-icon-Plus'>新增设备</el-button>
</template> </template>
</TableHeader> </TableHeader>
<Table ref="tableRef"></Table> <Table ref='tableRef'></Table>
<el-dialog <el-dialog
:title="dialogTitle" :title='dialogTitle'
v-model="dialogFormVisible" v-model='dialogFormVisible'
:close-on-click-modal="false" :close-on-click-modal='false'
width="600px" width='600px'
:before-close="resetForm" :before-close='resetForm'
> >
<el-form :model="form" label-width="120px" :rules="rules" ref="ruleFormRef"> <el-form :model='form' label-width='120px' :rules='rules' ref='ruleFormRef'>
<el-form-item label="设备名称:" prop="name"> <el-form-item label='设备名称:' prop='name'>
<el-input <el-input
v-model="form.name" v-model='form.name'
autocomplete="off" autocomplete='off'
clearable clearable
placeholder="请输入(项目名称+设备名称)" placeholder='请输入(项目名称+设备名称)'
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="网络设备ID:" prop="ndid" class="top"> <el-form-item label='网络设备ID:' prop='ndid' class='top'>
<el-input v-model="form.ndid" autocomplete="off" placeholder="请输入"></el-input> <el-input v-model='form.ndid' autocomplete='off' placeholder='请输入'></el-input>
</el-form-item> </el-form-item>
<el-form-item label="设备类型:" prop="devType" class="top"> <el-form-item label='设备类型:' prop='devType' class='top'>
<el-select <el-select
v-model="form.devType" v-model='form.devType'
placeholder="请选择" placeholder='请选择'
style="width: 100%" style='width: 100%'
@change="formDevTypeChange" @change='formDevTypeChange'
clearable clearable
> >
<el-option <el-option
v-for="item in devTypeOptions" v-for='item in devTypeOptions'
:key="item.value" :key='item.value'
:label="item.label" :label='item.label'
:value="item.value" :value='item.value'
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="设备型号:" prop="devModel" class="top"> <el-form-item label='设备型号:' prop='devModel' class='top'>
<el-select v-model="form.devModel" placeholder="请选择" style="width: 100%" clearable> <el-select v-model='form.devModel' placeholder='请选择' style='width: 100%' clearable>
<el-option <el-option
v-for="item in formDevModelOptionsFilter" v-for='item in formDevModelOptionsFilter'
:key="item.value" :key='item.value'
:label="item.label" :label='item.label'
:value="item.value" :value='item.value'
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="通讯协议:" prop="devAccessMethod" class="top"> <el-form-item label='通讯协议:' prop='devAccessMethod' class='top'>
<el-select v-model="form.devAccessMethod" placeholder="请选择" style="width: 100%" clearable> <el-select v-model='form.devAccessMethod' placeholder='请选择' style='width: 100%' clearable>
<el-option label="MQTT" value="MQTT"></el-option> <el-option label='MQTT' value='MQTT'></el-option>
<el-option label="CLD" value="cloud"></el-option> <el-option label='CLD' value='cloud'></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="合同号:" prop="cntractNo" class="top"> <el-form-item label='合同号:' prop='cntractNo' class='top'>
<el-input v-model="form.cntractNo" autocomplete="off" placeholder="请输入"></el-input> <el-input v-model='form.cntractNo' autocomplete='off' placeholder='请输入'></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="resetForm"> </el-button> <el-button @click='resetForm'> </el-button>
<el-button type="primary" @click="onSubmit"> </el-button> <el-button type='primary' @click='onSubmit'> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<div class="qrcode-label"> <div class='qrcode-label'>
<div class="qrcode-label-title">{{ deivce.mac }}</div> <div class='qrcode-label-title'>{{ deivce.mac }}</div>
<img class="qrcode-label-img" alt="二维码加载失败" :src="deivce.qrPath" /> <img class='qrcode-label-img' alt='二维码加载失败' :src='deivce.qrPath' />
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang='ts'>
import { ref, onMounted, provide, computed, reactive, nextTick } from 'vue' import { ref, onMounted, provide, computed, reactive, nextTick } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue' import Table from '@/components/table/index.vue'
@@ -146,6 +146,7 @@ import {
} from '@/api/cs-system-boot/device' } from '@/api/cs-system-boot/device'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import { fullUrl } from '@/utils/common' import { fullUrl } from '@/utils/common'
defineOptions({ defineOptions({
name: 'govern/log/debug' name: 'govern/log/debug'
}) })
@@ -261,7 +262,7 @@ const tableStore = new TableStore({
buttons: [ buttons: [
{ {
name: 'edit', name: 'edit',
title:'编辑', title: '编辑',
type: 'primary', type: 'primary',
icon: 'el-icon-Edit', icon: 'el-icon-Edit',
render: 'tipButton', render: 'tipButton',
@@ -430,9 +431,10 @@ onMounted(() => {
}, 100) }, 100)
}) })
const addMenu = () => {} const addMenu = () => {
}
</script> </script>
<style lang="scss" scoped> <style lang='scss' scoped>
.qrcode-label { .qrcode-label {
position: absolute; position: absolute;
top: 0; top: 0;