fix: bug#24641、24804、25111、22958、24806、25377、25597

* fix: 优化物联卡批量导入数量统计;优化物联卡状态查询列表

* fix: 24641、24804、25111、22958、24806、25377、25597

* feat: 修改区域管理删除逻辑

* fix: bug#24628、25704

* fix: bug#24545

* fix: bug#25414

* fix: bug#24479

* fix: bug#23410

* fix: bug#24404

* fix: bug#25374

* fix: bug#25062

* fix: bug#22409

* fix: bug#25525、26083

* fix: bug#26186

* fix: bug#26233
This commit is contained in:
XieYongHong 2024-05-30 10:34:14 +08:00 committed by GitHub
parent bc0c4fa55b
commit 251c7b982b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 1295 additions and 1143 deletions

View File

@ -1,3 +1,3 @@
#!/usr/bin/env bash
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.2.0-SNAPSHOT .
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.2.0-SNAPSHOT.
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.2.0-SNAPSHOT

View File

@ -38,8 +38,14 @@ export const detail = (id:string) => server.get(`/alarm/record/${id}`);
/**
*
*/
export const queryHistoryLogList = (alarmConfigId: string, data:any) => server.post(`/alarm/history/${alarmConfigId}/_query`,data);
/**
*
*/
export const queryHistoryList = (data:any) => server.post('/alarm/history/_query',data);
/**
*
*/

View File

@ -1,7 +1,7 @@
import server from '@/utils/request';
// 获取全部地区(树结构)
export const getRegionTree = (): Promise<any> => server.post(`/area/_all/tree`);
export const getRegionTree = (data?: any): Promise<any> => server.post(`/area/_all/tree`, data);
// 校验名称是否存在
export const validateName = (name: string, id?: string): Promise<any> => server.get(`/area/name/_validate?name=${name}${id ? `&id=${id}` : ''}`);
@ -20,3 +20,5 @@ export const updateRegion = (data: any): Promise<any> => server.patch(`/area`, d
// 获取全部内置地区(树结构)
export const getBuiltinRegionTree = (data: any): Promise<any> => server.post(`/area/builtin/_all/tree`, data);
export const queryAreaCount = (id: string) => server.get(`/ams/asset/area/${id}/_count`)

View File

@ -20,6 +20,7 @@ export const getPrimissTree_api = (id: string): Promise<any> => server.get(`/men
// 更新角色对应的权限树
export const updatePrimissTree_api = (id: string, data:object): Promise<any> => server.put(`/menu/role/${id}/_grant`,data);
export const clearPrimissTree_api = (id: string, data?:object): Promise<any> => server.put(`/menu/role/${id}/iot/clear-grant`,data);
// 获取用户列表
export const getUserByRole_api = (data: any): Promise<any> => server.post(`/user/_query/`, data);

View File

@ -22,7 +22,7 @@ import '@vuemap/vue-amap/dist/style.css';
import { getAMapUiPromise } from './utils';
import { useSystem } from '@/store/system';
const emit = defineEmits('init')
const emit = defineEmits(['init'])
const system = useSystem();
interface AMapProps {
@ -33,7 +33,9 @@ interface AMapProps {
const amapKey = system.$state.configInfo.amap?.apiKey;
initAMapApiLoader({
key: amapKey || '',
key: amapKey || 'c86c1b22c6b223e3ed08815532676445',
securityJsCode: 'b0efcf1ce14cbf2d56d3cde630cd19cf',
plugins: ['AMap.DistrictSearch'],
});
const props = defineProps({

View File

@ -56,7 +56,7 @@ export const defaultBranches = [
then: [],
executeAnyway: true,
branchId: Math.floor(Math.random() * 100000000),
branchName:'条件1'
branchName:''
},
];

View File

@ -1,4 +1,4 @@
<template lang="">
<template>
<j-modal
:title="data.id ? '编辑' : '新增'"
ok-text="确认"
@ -9,189 +9,190 @@
@cancel="handleCancel"
@ok="handleOk"
>
<j-form
class="form"
layout="vertical"
:model="formData"
name="basic"
autocomplete="off"
>
<a-spin :spinning="_loading">
<j-form
class="form"
layout="vertical"
:model="formData"
name="basic"
autocomplete="off"
>
<j-row :gutter="[24, 0]">
<j-col :span="24">
<j-form-item label="名称" v-bind="validateInfos.name">
<j-input
placeholder="请输入名称"
v-model:value="formData.name"
/></j-form-item>
</j-col>
<j-col :span="24"
><j-form-item
label="所属产品"
v-bind="validateInfos.productId"
>
<j-select
v-model:value="formData.productId"
:options="productOptions"
placeholder="请选择所属产品"
allowClear
show-search
:filter-option="filterOption"
/> </j-form-item
></j-col>
<j-col :span="12"
><j-form-item label="版本号" v-bind="validateInfos.version">
<j-input
placeholder="请输入版本号"
v-model:value="formData.version" /></j-form-item
></j-col>
<j-col :span="12"
><j-form-item
label="版本序号"
v-bind="validateInfos.versionOrder"
>
<j-input-number
placeholder="请输入版本序号"
style="width: 100%"
:min="1"
:max="99999"
v-model:value="
<j-col :span="24">
<j-form-item label="名称" v-bind="validateInfos.name">
<j-input
placeholder="请输入名称"
v-model:value="formData.name"
/></j-form-item>
</j-col>
<j-col :span="24"
><j-form-item
label="所属产品"
v-bind="validateInfos.productId"
>
<j-select
v-model:value="formData.productId"
:options="productOptions"
placeholder="请选择所属产品"
allowClear
show-search
:filter-option="filterOption"
/> </j-form-item
></j-col>
<j-col :span="12"
><j-form-item label="版本号" v-bind="validateInfos.version">
<j-input
placeholder="请输入版本号"
v-model:value="formData.version" /></j-form-item
></j-col>
<j-col :span="12"
><j-form-item
label="版本序号"
v-bind="validateInfos.versionOrder"
>
<j-input-number
placeholder="请输入版本序号"
style="width: 100%"
:min="1"
:max="99999"
v-model:value="
formData.versionOrder
" /></j-form-item
></j-col>
<j-col :span="12"
><j-form-item
label="签名方式"
v-bind="validateInfos.signMethod"
>
<j-select
v-model:value="formData.signMethod"
:options="[
></j-col>
<j-col :span="12"
><j-form-item
label="签名方式"
v-bind="validateInfos.signMethod"
>
<j-select
v-model:value="formData.signMethod"
:options="[
{ label: 'MD5', value: 'md5' },
{ label: 'SHA256', value: 'sha256' },
]"
placeholder="请选择签名方式"
allowClear
show-search
:filter-option="filterOption"
@change="changeSignMethod"
/>
</j-form-item>
</j-col>
<j-col :span="12"
><j-form-item v-bind="validateInfos.sign">
<template #label>
签名
<j-tooltip title="请输入本地文件进行签名加密后的值">
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</template>
<j-input
placeholder="请输入签名"
v-model:value="formData.sign" /></j-form-item
placeholder="请选择签名方式"
allowClear
show-search
:filter-option="filterOption"
@change="changeSignMethod"
/>
</j-form-item>
</j-col>
<j-col :span="12"
><j-form-item v-bind="validateInfos.sign">
<template #label>
签名
<j-tooltip title="请输入本地文件进行签名加密后的值">
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</template>
<j-input
placeholder="请输入签名"
v-model:value="formData.sign" /></j-form-item
></j-col>
<j-col :span="24">
<j-form-item label="固件上传" v-bind="validateInfos.url">
<FileUpload
v-model:modelValue="formData.url"
v-model:extraValue="extraValue"
/> </j-form-item
></j-col>
<j-col :span="24">
<j-form-item label="固件上传" v-bind="validateInfos.url">
<FileUpload
v-model:modelValue="formData.url"
v-model:extraValue="extraValue"
/> </j-form-item
></j-col>
<j-col :span="24">
<j-form-item
label="其他配置"
v-bind="validateInfos.properties"
>
<j-form
:class="
<j-col :span="24">
<j-form-item
label="其他配置"
v-bind="validateInfos.properties"
>
<j-form
:class="
dynamicValidateForm.properties.length !== 0 &&
'formRef'
"
ref="formRef"
name="dynamic_form_nest_item"
:model="dynamicValidateForm"
>
<div
class="formRef-content"
v-for="(
ref="formRef"
name="dynamic_form_nest_item"
:model="dynamicValidateForm"
>
<div
class="formRef-content"
v-for="(
propertie, index
) in dynamicValidateForm.properties"
:key="propertie.keyid"
>
<j-form-item
:label="index === 0 && 'Key'"
class="formRef-form-item"
:name="['properties', index, 'id']"
:rules="{
:key="propertie.keyid"
>
<j-form-item
:label="index === 0 && 'Key'"
class="formRef-form-item"
:name="['properties', index, 'id']"
:rules="{
required: true,
message: '请输入KEY',
}"
>
<j-input
v-model:value="propertie.id"
placeholder="请输入KEY"
/>
</j-form-item>
<j-form-item
:label="index === 0 && 'Value'"
class="formRef-form-item"
:name="['properties', index, 'value']"
:rules="{
>
<j-input
v-model:value="propertie.id"
placeholder="请输入KEY"
/>
</j-form-item>
<j-form-item
:label="index === 0 && 'Value'"
class="formRef-form-item"
:name="['properties', index, 'value']"
:rules="{
required: true,
message: '请输入VALUE',
}"
>
<j-input
v-model:value="propertie.value"
placeholder="请输入VALUE"
/>
</j-form-item>
<j-form-item
:label="index === 0 && '操作'"
class="formRef-form-item"
style="width: 10%"
>
<j-popconfirm
title="确认删除吗?"
ok-text="确认"
cancel-text="取消"
@confirm="removeList(propertie)"
>
<AIcon type="DeleteOutlined" />
</j-popconfirm>
</j-form-item>
</div>
<j-form-item class="formRef-form-item-add">
<j-button type="dashed" block @click="addList">
<AIcon type="PlusOutlined" />
添加
</j-button>
</j-form-item>
</j-form>
</j-form-item></j-col
>
<j-input
v-model:value="propertie.value"
placeholder="请输入VALUE"
/>
</j-form-item>
<j-form-item
:label="index === 0 && '操作'"
class="formRef-form-item"
style="width: 10%"
>
<j-popconfirm
title="确认删除吗?"
ok-text="确认"
cancel-text="取消"
@confirm="removeList(propertie)"
>
<AIcon type="DeleteOutlined" />
</j-popconfirm>
</j-form-item>
</div>
<j-form-item class="formRef-form-item-add">
<j-button type="dashed" block @click="addList">
<AIcon type="PlusOutlined" />
添加
</j-button>
</j-form-item>
</j-form>
</j-form-item></j-col
>
<j-col :span="24">
<j-form-item
label="说明"
v-bind="validateInfos.description"
>
<j-col :span="24">
<j-form-item
label="说明"
v-bind="validateInfos.description"
>
<j-textarea
placeholder="请输入说明"
v-model:value="formData.description"
:maxlength="200"
:rows="3"
showCount
/> </j-form-item
<j-textarea
placeholder="请输入说明"
v-model:value="formData.description"
:maxlength="200"
:rows="3"
showCount
/> </j-form-item
></j-col>
</j-row>
</j-form>
</j-form>
</a-spin>
</j-modal>
</template>
<script lang="ts" setup>
import { message, Form } from 'jetlinks-ui-components';
import { getImage } from '@/utils/comm';
import { Form } from 'jetlinks-ui-components';
import FileUpload from './FileUpload.vue';
import {
save,
@ -223,6 +224,7 @@ const addList = () => {
};
const loading = ref(false);
const _loading = ref(false);
const useForm = Form.useForm;
const productOptions = ref([]);
@ -282,6 +284,19 @@ const validatorVersionOrder = async (_: Record<string, any>, value: string) => {
}
};
const validatorProductExist = async (_: Record<string, any>, value: string) => {
if (!value) {
return Promise.resolve();
} else {
const dt = productOptions.value.find((i: any) => i.value === value)
if(dt){
return Promise.resolve();
} else {
return Promise.reject('当前产品不存在,请选择产品')
}
}
};
const validatorVersionValue = async (_rule:any,value:any) => {
return new Promise (async(resolve,reject)=>{
const posReg = /^[1-9]\d*$/;
@ -301,7 +316,10 @@ const { resetFields, validate, validateInfos } = useForm(
{ required: true, message: '请输入名称' },
{ max: 64, message: '最多可输入64个字符' },
],
productId: [{ required: true, message: '请选择所属产品' }],
productId: [
{ required: true, message: '请选择所属产品' },
{ validator: validatorProductExist, trigger: 'blur' }
],
version: [
{ required: true, message: '请输入版本号' },
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
@ -368,11 +386,13 @@ const changeSignMethod = () => {
};
onMounted(() => {
_loading.value = true
queryProduct({
paging: false,
terms: [{ column: 'state', value: 1 }],
sorts: [{ name: 'createTime', order: 'desc' }],
}).then((resp: any) => {
_loading.value = false
productOptions.value = resp.result.map((item: any) => ({
value: item.id,
label: item.name,

View File

@ -1,4 +1,4 @@
<template lang="">
<template>
<j-modal
:title="data.id ? '查看' : '新增' + '任务'"
ok-text="确认"

View File

@ -12,26 +12,36 @@
</PermissionButton>
</template>
<j-descriptions-item label="设备ID">
<div style="display: flex">
<div style="flex: 1">
<j-ellipsis> {{ instanceStore.current?.id }} </j-ellipsis>
<div style="display: flex">
<div style="flex: 1">
<j-ellipsis> {{ instanceStore.current?.id }} </j-ellipsis>
</div>
<div
v-if="
instanceStore.current?.accessProvider ===
'plugin_gateway'
"
>
<j-tooltip>
<template #title>
<p>
通过调用SDK或HTTP请求的方式接入第三方系统设备数据时第三方系统与平台当前设备对应的设备ID
</p>
如双方ID值一致则无需填写
</template>
<a
v-if="!inklingDeviceId"
type="link"
@click="giveAnInkling"
>
未映射
</a>
<a v-else type="link" @click="inkingVisible = true">
已映射
</a>
</j-tooltip>
</div>
</div>
<div v-if='instanceStore.current?.accessProvider === "plugin_gateway"'>
<j-tooltip>
<template #title>
<p>通过调用SDK或HTTP请求的方式接入第三方系统设备数据时第三方系统与平台当前设备对应的设备ID</p>
如双方ID值一致则无需填写
</template>
<a v-if="!inklingDeviceId" type='link' @click='giveAnInkling'>
未映射
</a>
<a v-else type='link' @click='inkingVisible = true'>
已映射
</a>
</j-tooltip>
</div>
</div>
</j-descriptions-item>
<j-descriptions-item label="产品名称">{{
instanceStore.current?.productName
@ -51,29 +61,27 @@
<j-descriptions-item label="创建时间">{{
instanceStore.current?.createTime
? moment(instanceStore.current?.createTime).format(
'YYYY-MM-DD HH:mm:ss',
)
'YYYY-MM-DD HH:mm:ss',
)
: ''
}}</j-descriptions-item>
<j-descriptions-item label="注册时间">{{
instanceStore.current?.registerTime
? moment(instanceStore.current?.registerTime).format(
'YYYY-MM-DD HH:mm:ss',
)
'YYYY-MM-DD HH:mm:ss',
)
: ''
}}</j-descriptions-item>
<j-descriptions-item label="最后上线时间">{{
instanceStore.current?.onlineTime
? moment(instanceStore.current?.onlineTime).format(
'YYYY-MM-DD HH:mm:ss',
)
'YYYY-MM-DD HH:mm:ss',
)
: ''
}}</j-descriptions-item>
<j-descriptions-item
label="父设备"
v-if="
instanceStore.current?.deviceType?.value === 'childrenDevice'
"
v-if="instanceStore.current?.deviceType?.value === 'childrenDevice'"
>{{ instanceStore.current?.parentId }}</j-descriptions-item
>
<j-descriptions-item label="说明">{{
@ -99,14 +107,14 @@
@close="visible = false"
@save="saveBtn"
/>
<InkingModal
v-if='inkingVisible'
:id='inklingDeviceId'
:accessId='instanceStore.current.accessId'
:pluginId="channelId"
@cancel="inkingVisible = false"
@submit='saveInkling'
/>
<InkingModal
v-if="inkingVisible"
:id="inklingDeviceId"
:accessId="instanceStore.current.accessId"
:pluginId="channelId"
@cancel="inkingVisible = false"
@submit="saveInkling"
/>
</template>
<script lang="ts" setup>
@ -115,16 +123,16 @@ import Save from '../../Save/index.vue';
import Config from './components/Config/index.vue';
import Tags from './components/Tags/index.vue';
import Relation from './components/Relation/index.vue';
import InkingModal from './components/InklingModal'
import InkingModal from './components/InklingModal';
import moment from 'moment';
import { detail as queryPluginAccessDetail } from '@/api/link/accessConfig'
import { getPluginData } from '@/api/link/plugin'
import { detail as queryPluginAccessDetail } from '@/api/link/accessConfig';
import { getPluginData } from '@/api/link/plugin';
const visible = ref<boolean>(false);
const inkingVisible = ref<boolean>(false);
const instanceStore = useInstanceStore();
const inklingDeviceId = ref()
const channelId = ref()
const inklingDeviceId = ref();
const channelId = ref();
const saveBtn = () => {
if (instanceStore.current?.id) {
@ -134,36 +142,51 @@ const saveBtn = () => {
};
const saveInkling = (id: string) => {
if (instanceStore.current?.id) {
instanceStore.refresh(instanceStore.current?.id);
}
channelId.value = id
queryInkling()
inkingVisible.value = false
}
if (instanceStore.current?.id) {
instanceStore.refresh(instanceStore.current?.id);
}
channelId.value = id;
queryInkling();
inkingVisible.value = false;
};
const giveAnInkling = () => {
inkingVisible.value = true
}
inkingVisible.value = true;
};
const queryInkling = () => {
if (instanceStore.current?.accessProvider === 'plugin_gateway') {
queryPluginAccessDetail(instanceStore.current?.accessId).then(async res => {
if (res.success) {
channelId.value = res.result.channelId
const pluginRes = await getPluginData('device',instanceStore.current?.accessId, instanceStore.current?.id)
if (pluginRes.success) {
inklingDeviceId.value = pluginRes.result?.externalId
if (instanceStore.current?.accessProvider === 'plugin_gateway') {
queryPluginAccessDetail(instanceStore.current?.accessId).then(
async (res) => {
if (res.success) {
channelId.value = res.result.channelId;
const pluginRes = await getPluginData(
'device',
instanceStore.current?.accessId,
instanceStore.current?.id,
);
if (pluginRes.success) {
inklingDeviceId.value = pluginRes.result?.externalId;
}
}
},
);
}
};
onMounted(() => {
//
if (instanceStore?.current?.id) {
instanceStore.refresh(instanceStore.current.id);
}
});
watch(
() => instanceStore.current?.id,
() => {
if (instanceStore.current?.id) {
queryInkling();
}
}
})
}
}
watch(() => instanceStore.current?.id, () => {
if (instanceStore.current?.id) {
queryInkling()
}
}, { immediate: true })
},
{ immediate: true },
);
</script>

View File

@ -638,6 +638,7 @@ const getGuide = async (isDriver1: boolean = false) => {
};
const checkAccess = async (data: any) => {
console.log(data)
visible.value = false
accessId.value = data.access.id
access.value = data.access
@ -648,6 +649,20 @@ const checkAccess = async (data: any) => {
metadata.value = data.metadata?.[0] || {
properties: []
}
if (metadata.value?.properties) {
metadata.value?.properties.forEach((item) => {
if (
item.name === '流传输模式' &&
(!productStore.current?.configuration ||
!productStore.current?.configuration.hasOwnProperty(
item.property,
))
) {
formData.data[item.property] =
item.type.expands?.defaultValue;
}
});
}
if (data.access.channel === 'plugin') { //
markdownToHtml.value = ''
productTypes.value = data.productTypes.map(item => ({ ...item, label: item.name, value: item.id}))

View File

@ -369,12 +369,12 @@ onMounted(() => {
onUnmounted(() => {
if (_delTag.value && dataSourceCache.value.length) {
//
const arr = dataSourceCache.value.filter((i: any) => i?.plugin).map((item: any) => {
// .filter((i: any) => i?.plugin)
const arr = dataSourceCache.value.map((item: any) => {
return {
metadataType: 'property',
metadataId: item.id,
originalId: item.plugin,
originalId: item.plugin || null,
}
})
onMapData(arr)

View File

@ -892,6 +892,17 @@ const handleImport = async () => {
// _object as DeviceMetadata,
// );
// console.log(copyOperateLimits,_object); //
Object.keys(_object).forEach((i: any) => {
if (i === 'functions') {
_object[i].forEach((a: any) => {
a?.inputs?.forEach((item: any) => {
item.expands = {
required: false,
};
});
});
}
});
const params = {
id,
metadata: JSON.stringify(_object),

View File

@ -4,7 +4,6 @@ export const testProperties = (data:any) =>{
}
export const testType = (data:any,index:number,isArray?:boolean,isObject?:boolean)=>{
console.log(data,index)
if(data.type === 'boolean'){
if(!data?.trueText || !data?.trueValue || !data?.falseText || !data?.falseValue){
onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失必填属性`,'error')
@ -39,7 +38,6 @@ export const testType = (data:any,index:number,isArray?:boolean,isObject?:boolea
}
}
if(data.type === 'object' && !isArray && !isObject){
console.log(data,'data123')
if(data?.properties?.length > 0){
return testObject(data.properties,index)
}else{

View File

@ -71,17 +71,17 @@
<j-descriptions-item label="总流量">{{
detail.totalFlow
? detail.totalFlow.toFixed(2) + ' M'
: ''
: '0 M'
}}</j-descriptions-item>
<j-descriptions-item label="使用流量">{{
detail.usedFlow
? detail.usedFlow.toFixed(2) + ' M'
: ''
: '0 M'
}}</j-descriptions-item>
<j-descriptions-item label="剩余流量">{{
detail.residualFlow
? detail.residualFlow.toFixed(2) + ' M'
: ''
: '0 M'
}}</j-descriptions-item>
<j-descriptions-item label="状态">
{{
@ -282,12 +282,12 @@ const getData = (
};
/**
* 查询当月本年数据
* 查询当月本年数据
*/
const getDataTotal = () => {
const dTime = [
moment(new Date()).startOf('day').valueOf(),
moment(new Date()).endOf('day').valueOf(),
moment(new Date()).subtract(1, 'day').startOf('day').valueOf(),
moment(new Date()).subtract(1, 'day').endOf('day').valueOf(),
];
const mTime = [
moment().startOf('month').valueOf(),

View File

@ -150,10 +150,10 @@ const handleImport = async (file: any) => {
event.onmessage = (e) => {
const result = JSON.parse(e.data);
if (result.success) {
successNumber.value++;
successNumber.value += result.result?.total || 1;
} else {
if (result.rowNumber !== -1) {
failNumber.value++;
failNumber.value += result.result?.total || 1;
message.push({
rowNumber: `${result.rowNumber}`,
message: result.message,

View File

@ -575,10 +575,12 @@ const columns = [
search: {
type: 'select',
options: [
{ label: '未同步', value: 'notReady' },
{ label: '同步失败', value: 'error' },
{ label: '激活', value: 'using' },
{ label: '未激活', value: 'toBeActivated' },
{ label: '停机', value: 'deactivate' },
{ label: '其它', value: 'using,toBeActivated,deactivate' },
{ label: '其它', value: 'other' },
],
},
},
@ -759,20 +761,7 @@ const getActions = (
};
const handleSearch = (e: any) => {
const newParams = (e?.terms as any[])?.map((item1) => {
item1.terms = item1.terms.map((item2: any) => {
if (
['cardStateType'].includes(item2.column) &&
!['using', 'toBeActivated', 'deactivate'].includes(item2.value)
) {
//
item2.termType = item2.termType === 'not' ? 'in' : 'nin';
}
return item2;
});
return item1;
});
params.value = { terms: newParams || [] };
params.value = { terms: e?.terms || [] };
};
const onSelectChange = (keys: string[], rows: []) => {

View File

@ -415,7 +415,7 @@ const formData = ref({
onvifUsername: '',
},
//
streamMode: 'UDP',
streamMode: '',
manufacturer: '',
model: '',
firmware: '',
@ -442,12 +442,13 @@ const getProductList = async () => {
const { result } = await DeviceApi.queryProductList(params);
productList.value = result;
};
getProductList();
const handleProductChange = () => {
formData.value.others.access_pwd =
productList.value.find((f: any) => f.id === formData.value.productId)
?.configuration.access_pwd || '';
formData.value.streamMode = productList.value.find((f: any) => f.id === formData.value.productId)
?.configuration.stream_mode || '';
};
/**
@ -463,11 +464,24 @@ const getDetail = async () => {
const res = await DeviceApi.detail(route.query.id as string);
Object.assign(formData.value, res.result);
formData.value.channel = res.result.provider;
console.log(formData.value,'formData')
if (formData.value.productId) {
const productData = productList.value.find((i: any) => {
return i.id === formData.value.productId;
});
if (productData) {
formData.value.others.access_pwd = formData.value.others.access_pwd
? formData.value.others.access_pwd
: productData?.configuration?.access_pwd;
formData.value.streamMode = formData.value.streamMode
? formData.value.streamMode
: productData?.configuration?.stream_mode;
}
}
};
onMounted(() => {
getDetail();
getProductList();
});
/**
@ -493,11 +507,7 @@ const handleSubmit = () => {
: { id, streamMode, manufacturer, model, firmware, ...extraParams };
} else if (formData.value.channel === 'gb28181-2016') {
//
others = omit(others, [
'onvifUrl',
'onvifPassword',
'onvifUsername',
]);
others = omit(others, ['onvifUrl', 'onvifPassword', 'onvifUsername']);
const getParmas = () => {
if (others?.stream_mode) {
others.stream_mode = streamMode;
@ -516,8 +526,16 @@ const handleSubmit = () => {
} else {
others = omit(others, ['access_pwd']);
params = !id
? {others,...extraParams}
: { id, streamMode, manufacturer, model, firmware,others, ...extraParams };
? { others, ...extraParams }
: {
id,
streamMode,
manufacturer,
model,
firmware,
others,
...extraParams,
};
}
formRef.value

View File

@ -202,6 +202,8 @@ const columns = [
{
title: '变量',
dataIndex: 'id',
width: 100,
ellipsis: true,
scopedSlots: { customRender: 'id' },
},
{

View File

@ -203,6 +203,8 @@ const columns = [
{
title: '变量',
dataIndex: 'id',
width: 100,
ellipsis: true,
scopedSlots: { customRender: 'id' },
},
{

View File

@ -103,6 +103,7 @@ const columns = [
title: '变量',
dataIndex: 'id',
width: 80,
ellipsis: true
},
{
title: '名称',
@ -151,6 +152,7 @@ const handleTypeChange = (record: IVariable) => {
<style lang="less" scoped>
.table-wrapper {
background-color: #1d39c4;
.has-error {
border-color: rgba(255, 77, 79);
&:focus {

View File

@ -662,6 +662,7 @@
v-model:modelValue="
formData.template.body
"
language="JavaScript"
/>
</div>
</j-form-item>

View File

@ -147,39 +147,40 @@ const columns = [
dataIndex: 'name',
key: 'name',
search: {
type: 'select',
options: async () => {
const res = await query(
{
sorts: [
{
name: 'createTime',
order: 'desc',
},
],
terms: [
{
column: 'id',
termType: 'alarm-bind-rule$not',
value: props.id,
type: 'and',
},
{
column: 'triggerType',
termType: 'eq',
value: props.type === 'other' ? undefined : 'device',
},
]
}
);
if (res.status === 200) {
return res.result.data.map((item: any) => ({
label: item.name,
value: item.id,
}));
}
return []
}
type: 'string',
// rename: "id",
// options: async () => {
// const res = await query(
// {
// sorts: [
// {
// name: 'createTime',
// order: 'desc',
// },
// ],
// terms: [
// {
// column: 'id',
// termType: 'alarm-bind-rule$not',
// value: props.id,
// type: 'and',
// },
// {
// column: 'triggerType',
// termType: 'eq',
// value: props.type === 'other' ? undefined : 'device',
// },
// ]
// }
// );
// if (res.status === 200) {
// return res.result.data.map((item: any) => ({
// label: item.name,
// value: item.id,
// }));
// }
// return []
// }
},
},
{

View File

@ -9,6 +9,7 @@
<JProTable
:columns="columns"
model="TABLE"
ref="tableRef"
:request="queryList"
:params="params"
:defaultParams="{
@ -45,7 +46,7 @@
</JProTable>
</FullPage>
<Info
v-if="visiable"
v-if="visible"
:data="current"
@close="close"
:description="description"
@ -54,20 +55,19 @@
</template>
<script lang="ts" setup>
import { detail, queryHistoryList } from '@/api/rule-engine/log';
import {detail, queryHistoryLogList} from '@/api/rule-engine/log';
import { detail as configurationDetail } from '@/api/rule-engine/configuration';
import { useRoute } from 'vue-router';
import dayjs from 'dayjs';
import type { ActionsType } from '@/components/Table/index.vue';
import { useAlarmStore } from '@/store/alarm';
import Info from './info.vue';
import { storeToRefs } from 'pinia';
import { useRouterParams } from '@/utils/hooks/useParams';
const route = useRoute();
const id = route.params?.id;
const { params: routerParams } = useRouterParams();
let visiable = ref(false);
let visible = ref(false);
let description = ref<string>();
const tableRef = ref()
const columns = [
{
title: '告警时间',
@ -98,7 +98,7 @@ const columns = [
const getActions = (
data: Partial<Record<string, any>>,
type?: 'table',
): ActionsType[] => {
): any[] => {
if (!data) {
return [];
}
@ -112,7 +112,7 @@ const getActions = (
icon: 'SearchOutlined',
onClick: () => {
current.value = data;
visiable.value = true;
visible.value = true;
},
},
];
@ -135,56 +135,63 @@ let details = ref(); // 告警记录的详情
* 获取详情列表
*/
const queryList = async (params: any) => {
const res = await queryHistoryList({
...params,
// sorts: [{ name: 'alarmTime', order: 'desc' }],
if(data.current?.alarmConfigId){
const res = await queryHistoryLogList(data.current?.alarmConfigId,{
...params,
// sorts: [{ name: 'alarmTime', order: 'desc' }],
});
if (res.status === 200) {
details.value = res.result.data[0];
return {
code: res.message,
result: {
data: res.result.data,
pageIndex: res.result.pageIndex,
pageSize: res.result.pageSize,
total: res.result.total,
},
status: res.status,
};
} else {
return {
code: 200,
result: {
data: [],
pageIndex: 0,
pageSize: 0,
total: 0,
},
status: 200,
};
details.value = res.result.data[0];
return {
code: res.message,
result: {
data: res.result.data,
pageIndex: res.result.pageIndex,
pageSize: res.result.pageSize,
total: res.result.total,
},
status: res.status,
};
}
} else {
return {
code: 200,
result: {
data: [],
pageIndex: 0,
pageSize: 0,
total: 0,
},
status: 200,
};
}
};
/**
* 根据id初始化数据
*/
watchEffect(async () => {
const res = await detail(id);
if (res.status === 200) {
data.current = res.result;
if (res.result.targetType === 'device') {
columns.splice(2, 0, {
dataIndex: 'targetName',
title: '告警设备',
key: 'targetName',
});
}
configurationDetail(res.result?.alarmConfigId).then((res: any) => {
if (res.status === 200) {
description.value = res.result?.description;
}
});
watch(() => id, async () => {
const res = await detail(id);
if (res.status === 200) {
data.current = res.result || {};
tableRef.value?.reload()
if (res.result?.targetType === 'device') {
columns.splice(2, 0, {
dataIndex: 'targetName',
title: '告警设备',
key: 'targetName',
});
}
});
configurationDetail(res.result?.alarmConfigId).then((res: any) => {
if (res.status === 200) {
description.value = res.result?.description;
}
});
}
}, {
deep: true,
immediate: true
})
const handleSearch = (_params: any) => {
params.value = _params;
};
@ -193,13 +200,13 @@ const handleSearch = (_params: any) => {
* 关闭模态弹窗
*/
const close = () => {
visiable.value = false;
visible.value = false;
};
watchEffect(() => {
current.value = details.value;
if (routerParams.value.detail && details.value) {
visiable.value = true;
visible.value = true;
}
});
</script>

View File

@ -120,13 +120,17 @@
{{ titleMap.get(slotProps.targetType) }}
</template>
<template #alarmTime="slotProps">
{{ dayjs(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')}}
{{
dayjs(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')
}}
</template>
<template #level="slotProps">
<Ellipsis style="width: calc(100% - 20px)">
{{ data.defaultLevel.find((i)=>{
return i.level === slotProps.level
}).title}}
{{
data.defaultLevel.find((i) => {
return i.level === slotProps.level;
}).title
}}
</Ellipsis>
</template>
<template #state="slotProps">
@ -229,26 +233,26 @@ titleMap.set('other', '其他');
titleMap.set('org', '组织');
const columns = [
{
title:'配置名称',
dataIndex:'alarmName',
key:'alarmName',
},{
title:'类型',
dataIndex:'targetType',
key:'targetType',
scopedSlots:true
title: '配置名称',
dataIndex: 'alarmName',
key: 'alarmName',
},
{
title:'关联场景联动',
dataIndex:'sourceName',
key:'sourceName',
title: '类型',
dataIndex: 'targetType',
key: 'targetType',
scopedSlots: true,
},
{
title: '关联场景联动',
dataIndex: 'sourceName',
key: 'sourceName',
},
{
title: '告警级别',
dataIndex: 'level',
key: 'level',
width:200,
width: 200,
search: {
type: 'select',
options: data.value.defaultLevel.map((item: any) => {
@ -258,7 +262,7 @@ const columns = [
};
}),
},
scopedSlots: true
scopedSlots: true,
},
{
title: '最近告警时间',
@ -300,49 +304,52 @@ const columns = [
const newColumns = computed(() => {
const otherColumns = {
title: '产品名称',
dataIndex: 'targetId',
key: 'targetId',
dataIndex: 'targetName',
key: 'targetName',
// search: {
// type: 'select',
// options: async () => {
// const termType = [
// {
// column: 'targetType',
// termType: 'eq',
// type: 'and',
// value: props.type,
// },
// ];
// if (props.id) {
// termType.push({
// termType: 'eq',
// column: 'alarmConfigId',
// value: props.id,
// type: 'and',
// });
// }
// const resp: any = await handleSearch({
// sorts: [{ name: 'alarmTime', order: 'desc' }],
// terms: termType,
// });
// const listMap: Map<string, any> = new Map();
// if (resp.status === 200) {
// resp.result.data.forEach((item) => {
// if (item.targetId) {
// listMap.set(item.targetId, {
// label: item.targetName,
// value: item.targetId,
// });
// }
// });
// return [...listMap.values()];
// }
// return [];
// },
// },
search: {
type: 'select',
options: async () => {
const termType = [
{
column: 'targetType',
termType: 'eq',
type: 'and',
value: props.type,
},
];
if (props.id) {
termType.push({
termType: 'eq',
column: 'alarmConfigId',
value: props.id,
type: 'and',
});
}
const resp: any = await handleSearch({
sorts: [{ name: 'alarmTime', order: 'desc' }],
terms: termType,
});
const listMap: Map<string, any> = new Map();
if (resp.status === 200) {
resp.result.data.forEach((item) => {
if (item.targetId) {
listMap.set(item.targetId, {
label: item.targetName,
value: item.targetId,
});
}
});
return [...listMap.values()];
}
return [];
},
type: 'string',
},
};

View File

@ -234,10 +234,10 @@ const getDashBoard = () => {
const _data = res.result as DashboardItem[];
state.today = _data.find(
(item) => item.group === 'today',
)?.data.value;
)?.data.value || 0;
state.thisMonth = _data.find(
(item) => item.group === 'thisMonth',
)?.data.value;
)?.data.value || 0;
currentMonAlarm.value[0].value = state.thisMonth;
const fifteenData = _data
.filter((item) => item.group === '15day')

View File

@ -72,7 +72,10 @@ watchEffect(() => {
const _item = _props.value?.find((i) => i.name === item?.id) || {};
return {
...item,
..._item,
...{
name: _item.id,
..._item.value
},
name: item.name,
};
});
@ -83,9 +86,11 @@ const onChange = () => {
const arr = [...dataSource.value].map((item) => {
return {
name: item.id,
source: item.source,
upperKey: item.upperKey || item.value,
value: item.value,
value: {
source: item.source,
upperKey: item.upperKey || item.value,
value: item.value,
},
};
});
emit('update:value', arr);

View File

@ -124,6 +124,7 @@ const sceneStore = useSceneStore();
const { data: formModel } = storeToRefs(sceneStore);
const formItemContext = Form.useInjectFormItemContext();
type Emit = {
(e: 'update:value', data: TermsType): void;
};
@ -163,6 +164,10 @@ const props = defineProps({
type: Number,
default: 0,
},
branches_Index:{
type: Number,
default: 0,
},
value: {
type: Object as PropType<TermsType>,
default: () => ({
@ -401,16 +406,15 @@ const columnSelect = (option: any) => {
paramsValue.value = newValue;
}
console.log(paramsValue, hasTypeChange);
handOptionByColumn(option);
emit('update:value', { ...paramsValue });
nextTick(() => {
formItemContext.onFieldChange();
});
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[
props.termsName
][0] = option.name;
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[
props.termsName
][1] = paramsValue.termType;
};
@ -463,7 +467,7 @@ const termsTypeSelect = (e: { key: string; name: string }) => {
paramsValue.value = newValue;
emit('update:value', { ...paramsValue });
formItemContext.onFieldChange();
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[
props.termsName
][1] = e.name;
};
@ -483,14 +487,14 @@ const valueSelect = (
}
emit('update:value', { ...newValues });
formItemContext.onFieldChange();
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[
props.termsName
][2] = labelObj;
};
const typeSelect = (e: any) => {
emit('update:value', { ...paramsValue });
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[
props.termsName
][3] = e.label;
};

View File

@ -193,7 +193,7 @@ const addGroup = () => {
then: [],
executeAnyway: true,
branchId: Math.floor(Math.random() * 100000000),
branchName:'条件'+ data.value.branches?.length
branchName:''
}
data.value.branches?.push(branchesItem)
data.value.branches?.push(null as any)

View File

@ -82,7 +82,6 @@ const save = async () => {
getDetail(route.query.id as string)
onUnmounted(() => {
console.log('scene-onUnmounted')
refresh?.()
})

View File

@ -108,10 +108,12 @@
tooltip="填写访问其它平台的地址"
/>
</template>
<j-input
<!-- <j-input
v-model:value="form.data.page.baseUrl"
placeholder="请输入接入地址"
/>
/> -->
<InputGroup v-model:value="form.data.page.baseUrl" style="width: 100%;"/>
</j-form-item>
<j-form-item
label="路由方式"
@ -1436,6 +1438,7 @@ import { cloneDeep, difference } from 'lodash-es';
import { useMenuStore } from '@/store/menu';
import { Rule } from 'ant-design-vue/lib/form';
import ApplyList from './ApplyList/index.vue';
import InputGroup from './InputGroup.vue'
const emit = defineEmits(['changeApplyType']);

View File

@ -0,0 +1,69 @@
<template>
<div>
<a-input
@change="onChange"
placeholder="请输入"
v-model:value="_value.last"
>
<template #addonBefore>
<a-select
@change="onChange"
v-model:value="_value.first"
:options="options"
style="width: 100px"
placeholder="请选择"
/>
</template>
</a-input>
</div>
</template>
<script setup name="InputGroup">
const props = defineProps({
value: {
type: String,
},
});
const emits = defineEmits(['update:value']);
const options = [
{
label: 'https://',
value: 'https://',
},
{
label: 'http://',
value: 'http://',
},
];
const _value = reactive({
first: 'https://',
last: undefined,
});
const onChange = () => {
emits('update:value', `${_value.first || ''}${_value.last || ''}`);
};
watch(
() => props.value,
(val) => {
if (val && typeof val === 'string') {
options.forEach((i) => {
if (val.startsWith(i.value)) {
_value.first = i.value;
_value.last = val.replace(i.value, '');
}
});
} else {
_value.first = 'https://';
_value.last = undefined;
}
},
{
immediate: true,
},
);
</script>
<style lang="less" scoped></style>

View File

@ -242,17 +242,6 @@ const typeOptions = ref<any[]>([]);
const visible = ref<boolean>(false);
const addMenuVisible = ref<boolean>(false);
onMounted(() => {
queryType().then((resp: any) => {
if (resp.status === 200) {
const arr = resp.result.map((item: any) => ({
label: item.name,
value: item.provider,
}));
typeOptions.value = arr;
}
});
});
const columns = [
{
title: '名称',
@ -272,17 +261,17 @@ const columns = [
search: {
type: 'select',
options: typeOptions,
// options: () =>
// new Promise((resolve) => {
// queryType().then((resp: any) => {
// resolve(
// resp.result.map((item: any) => ({
// label: item.name,
// value: item.provider,
// })),
// );
// });
// }),
options: () =>
new Promise((resolve) => {
queryType().then((resp: any) => {
resolve(
resp.result.map((item: any) => ({
label: item.name,
value: item.provider,
})),
);
});
}),
},
scopedSlots: true,
},

View File

@ -5,6 +5,7 @@
title="权限控制"
@cancel="emit('close')"
@ok="onSave"
:maskClosable="false"
>
<div class="alert">
<AIcon type="InfoCircleOutlined" />

View File

@ -1,5 +1,5 @@
<template>
<j-modal :width="644" visible title="配置详情" @cancel="emit('close')">
<j-modal :width="644" visible title="配置详情" @cancel="emit('close')" :maskClosable="false">
<div class="detail">
<div class="item">
<div class="label">通知方式</div>

View File

@ -5,6 +5,7 @@
title="配置通知方式"
@cancel="emit('close')"
:bodyStyle="{ padding: 0 }"
:maskClosable="false"
>
<div style="background-color: #f8f9fc; padding: 25px 100px">
<j-steps :current="current" size="small" @change="onChange">

View File

@ -63,8 +63,7 @@ export function getCodeText(
result[item.paramsName] = '';
break;
default: {
const properties = schemas[item.paramsType]
.properties as object;
const properties = schemas[item.paramsType]?.properties as object || {};
const newArr = Object.entries(properties).map(
(item: [string, any]) => ({
paramsName: item[0],

View File

@ -9,73 +9,101 @@
<AIcon type="SearchOutlined" style="color: rgba(0, 0, 0, 0.45)" />
</template>
</j-input>
<j-button @click="onAdd" type="primary" class="btn">新增区域</j-button>
<j-tree
class="draggable-tree"
draggable
block-node
v-if="treeData.length"
:tree-data="_treeData"
@drop="onDrop"
:defaultExpandAll="true"
:height="700"
:show-line="{ showLeafIcon: false }"
:show-icon="true"
>
<template #title="_data">
<div class="tree-box">
<div class="name">
<j-ellipsis>{{ _data?.name }}</j-ellipsis>
</div>
<div class="actions">
<j-space :size="8">
<j-tooltip title="重命名">
<j-button
@click.stop="onEdit(_data?.data)"
class="actions-btn"
type="link"
><AIcon type="EditOutlined"
/></j-button>
</j-tooltip>
<j-tooltip title="新增子区域">
<j-button
@click.stop="onAdd(_data?.data)"
class="actions-btn"
type="link"
><AIcon type="PlusCircleOutlined"
/></j-button>
</j-tooltip>
<j-tooltip title="删除">
<j-popconfirm @confirm="onRemove(_data?.id)">
<j-button
@click.stop
class="actions-btn"
type="link"
danger
><AIcon type="DeleteOutlined"
/></j-button>
</j-popconfirm>
</j-tooltip>
</j-space>
</div>
<div style="display: flex; gap: 8px; margin: 18px 0">
<j-button type="primary" class="btn" @click="() => onAdd()"
>新增区域</j-button
>
</div>
<div class="tree-content">
<ResizeObserver v-if="_treeData.length" @resize="divResize">
<div style="height: 100%; width: 100%">
<a-tree
class="draggable-tree"
draggable
block-node
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
:tree-data="_treeData"
:show-line="{ showLeafIcon: false }"
:show-icon="true"
:field-names="{ key: 'id' }"
:virtual="true"
:height="heightSize"
@drop="onDrop"
@select="areaSelect"
>
<template #title="_data">
<div class="tree-box">
<div class="name">
<j-ellipsis
>{{ _data?.name }} ({{
_data.code
}})</j-ellipsis
>
</div>
<div class="actions">
<j-space :size="8">
<j-tooltip title="编辑">
<j-button
@click.stop="onEdit(_data?.data)"
class="actions-btn"
type="link"
>
<AIcon type="EditOutlined" />
</j-button>
</j-tooltip>
<j-tooltip title="新增子区域">
<j-button
@click.stop="onAdd(_data?.data)"
class="actions-btn"
type="link"
>
<AIcon type="PlusCircleOutlined" />
</j-button>
</j-tooltip>
<j-tooltip title="删除">
<j-popconfirm
@confirm="onRemove(_data?.id)"
>
<j-button
@click.stop
class="actions-btn"
type="link"
danger
>
<AIcon type="DeleteOutlined" />
</j-button>
</j-popconfirm>
</j-tooltip>
</j-space>
</div>
</div>
</template>
</a-tree>
</div>
</template>
</j-tree>
<j-empty v-else style="margin-top: 150px" />
</ResizeObserver>
<div v-else class="tree-empty">
<j-empty />
</div>
</div>
<Save
:mode="mode"
v-if="visible"
:data="current"
:treeData="_treeData"
:areaTree="areaTree"
@save="onSave"
@close="onClose"
/>
</template>
<script lang="ts" setup>
import { onlyMessage } from '@/utils/comm';
import { debounce } from 'lodash-es';
import { cloneDeep, debounce } from 'lodash-es';
import { onMounted, ref, watch } from 'vue';
import Save from '../Save/index.vue';
import { getRegionTree, delRegion } from '@/api/system/region';
import { useArea } from '../hooks';
import ResizeObserver from 'ant-design-vue/lib/vc-resize-observer';
import { onlyMessage } from '@/utils/comm';
const treeData = ref<any[]>([]);
const _treeData = ref<any[]>([]);
@ -83,12 +111,22 @@ const visible = ref<boolean>(false);
const current = ref<any>({});
const mode = ref<'add' | 'edit'>('add');
const searchValue = ref<string>();
const expandedKeys = ref<string[]>([]);
const selectedKeys = ref<string[]>([]);
const heightSize = ref(550);
const type = ref<string | undefined>(undefined);
const { areaTree } = useArea();
const emit = defineEmits(['select']);
const filterTreeNodes = (tree: any[], condition: string) => {
return tree.filter((item) => {
if (item?.title && item.title.includes(condition)) {
if (item?.name && item.name.includes(condition)) {
return true;
}
if (item?.code && item.code.includes(condition)) {
return true;
}
@ -102,19 +140,43 @@ const filterTreeNodes = (tree: any[], condition: string) => {
});
};
const getTreeId = (data: any[], cb: (id: string) => void) => {
data.forEach((item) => {
if (item.children) {
cb?.(item.id);
getTreeId(item.children, cb);
}
});
};
const onSearch = debounce((v: string) => {
_treeData.value = filterTreeNodes(treeData.value, v);
});
_treeData.value = v
? filterTreeNodes(cloneDeep(treeData.value), v)
: cloneDeep(treeData.value);
expandedKeys.value = [];
if (v) {
getTreeId(_treeData.value, (id: string) => {
expandedKeys.value.push(id);
});
expandedKeys.value = [...expandedKeys.value];
}
}, 300);
const onSave = () => {
visible.value = false;
handleSearch()
handleSearch();
};
const onClose = () => {
visible.value = false;
};
const divResize = ({ height }) => {
setTimeout(() => {
heightSize.value = height;
}, 300);
};
const onEdit = (_data: any) => {
mode.value = 'edit';
current.value = _data;
@ -131,7 +193,21 @@ const onRemove = async (id: string) => {
const onAdd = (_data?: any) => {
mode.value = 'add';
current.value = _data ? _data : {};
const _children = _data ? _data.children || [] : _treeData.value;
const lastItem = _children.length ? _children[_children.length - 1] : null;
const sortIndex = lastItem ? lastItem.sortIndex + 1 : 1;
current.value = _data
? {
parentId: _data.id,
parentFullName: _data.fullName,
sortIndex: sortIndex,
}
: {
parentId: '',
parentFullName: '',
sortIndex: sortIndex,
};
visible.value = true;
};
@ -206,10 +282,27 @@ watch(
},
);
/**
* 区域选择
*/
const areaSelect = (key, { node }) => {
selectedKeys.value = key;
emit('select', node?.code);
};
const handleSearch = async () => {
const resp = await getRegionTree();
const resp = await getRegionTree({
paging: false,
sorts: [{ name: 'sortIndex', order: 'asc' }],
});
if (resp.success) {
treeData.value = resp?.result || [];
//
const dt = treeData.value?.[0];
if (dt) {
selectedKeys.value = dt?.id ? [dt?.id] : [];
emit('select', dt?.code);
}
}
};
@ -220,15 +313,36 @@ onMounted(() => {
<style lang="less" scoped>
.btn {
width: 100%;
margin: 18px 0;
flex: 1;
min-width: 0;
}
.tree-content {
display: flex;
flex-grow: 1;
height: 0;
width: 100%;
.tree-empty {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 100px;
width: 100%;
}
}
:deep(.ant-tree-node-content-wrapper) {
transform: translateY(-4px) !important;
}
.tree-box {
display: flex;
justify-content: space-between;
align-items: center;
.actions {
padding-right: 4px;
.actions-btn {
margin: 0;
padding: 0;
@ -236,5 +350,3 @@ onMounted(() => {
}
}
</style>

View File

@ -1,240 +1,72 @@
<template>
<div class="region-map">
<AMapComponent
@init="initMap"
>
<el-amap-polygon
v-if="overlay.type === 'polygon'"
:key="JSON.stringify(overlay.path || [])"
:editable="overlay.editable"
:path="overlay.path"
:visible="visible"
@addnode="polygonDraw"
@adjust="polygonDraw"
@init="overlaysInit"
@removenode="polygonDraw"
/>
<el-amap-circle
v-else-if="overlay.type === 'circle'"
:key="`${overlay.radius}_${JSON.stringify(overlay.path || [])}`"
:center="overlay.path"
:editable="overlay.editable"
:radius="overlay.radius"
:visible="visible"
@adjust="circleDraw"
@init="overlaysInit"
/>
<el-amap-rectangle
v-else-if="overlay.type === 'rectangle'"
:key="JSON.stringify(overlay.path || [])"
:bounds="overlay.path"
:editable="overlay.editable"
:visible="visible"
@adjust="rectangleDraw"
@init="overlaysInit"
/>
<el-amap-mouse-tool
v-else-if="overlay.type === 'create' && toolType"
:type="toolType"
@draw="toolDraw"
/>
</AMapComponent>
<div v-show="overlay.editable || overlay.type === 'create'" class="map-tool">
<div class="map-tool-content">
<div class="tool-item-group">
<div class="tool-item">
<j-tooltip title="双击保存描点" >
<AIcon type="QuestionCircleOutlined" />
</j-tooltip>
</div>
</div>
<div class="tool-item-group">
<div :class="{'tool-item': true, 'active': toolType === 'rectangle'}" @click="() => { drawOverlays('rectangle') }">
<j-tooltip title="矩形" >
<AIcon type="icon-huajuxing" />
</j-tooltip>
</div>
<div :class="{'tool-item': true, 'active': toolType === 'circle'}" @click="() => { drawOverlays('circle') }">
<j-tooltip title="圆" >
<AIcon type="icon-draw-circle" />
</j-tooltip>
</div>
<div :class="{'tool-item': true, 'active': toolType === 'polygon'}" @click="() => { drawOverlays('polygon') }">
<j-tooltip title="多边形" >
<AIcon type="icon-huaduobianxing" />
</j-tooltip>
</div>
</div>
<!-- <div class="tool-item-group">-->
<!-- <div class="tool-item">-->
<!-- <j-tooltip title="缩放" >-->
<!-- <AIcon type="GetwayOutlined" />-->
<!-- </j-tooltip>-->
<!-- </div>-->
<!-- <div class="tool-item">-->
<!-- <j-tooltip title="旋转" >-->
<!-- <AIcon type="GetwayOutlined" />-->
<!-- </j-tooltip>-->
<!-- </div>-->
<!-- </div>-->
<div class="tool-item-group">
<div :class="{'tool-item': true, 'disabled': historyList.length <= 1 }" @click="cancel">
<j-tooltip title="撤销" >
<AIcon type="RollbackOutlined" />
</j-tooltip>
</div>
<div class="tool-item">
<j-tooltip title="删除" @click="deleteOverlays">
<AIcon type="DeleteOutlined" />
</j-tooltip>
</div>
</div>
</div>
<AMapComponent @init="initMap"/>
<div class="region-map-loading" v-if="_loading || loading">
<a-spin :spinning="_loading || loading"></a-spin>
</div>
</div>
</template>
<script name="RegionMap" setup>
const props = defineProps({
path: {
type: Array,
default: () => []
},
radius: {
type: Number,
default: 0
},
type: {
selectCode: {
type: String,
default: 'polygon'
default: undefined
},
})
const emit = defineEmits('dragend')
const MapRef = ref()
const toolType = ref('')
const historyList = ref([])
const overlay = reactive({
type: props.type,
path: props.path,
center: [],
editable: false,
status: 0 // 0 1
})
const visible = computed(() => {
return true
})
const setHistory = (data) => {
if (historyList.value.length > 10) {
historyList.value.shift()
}
historyList.value.push(data)
}
const loading = ref(true)
const _loading = ref(true)
let polygon = null
let district = null
const initMap = (e) => {
loading.value = true
MapRef.value = e
loading.value = false
}
const polygonDraw = (e) => {
const target = e.target
const path = target.getPath()
setHistory({
type: 'polygon',
toolType: toolType.value,
editable: true,
path: path.map(item => [item.lng, item.lat]),
})
}
const circleDraw = (e) => {
setHistory({
type: 'circle',
toolType: toolType.value,
editable: true,
path: [e.lnglat.lng, e.lnglat.lat],
radius: e.radius
})
}
const rectangleDraw = (e) => {
const northEast = e.bounds.getNorthEast()
const southWest = e.bounds.getSouthWest()
const path = [
[southWest.lng, southWest.lat],
[northEast.lng, northEast.lat]
]
setHistory({
type: 'rectangle',
toolType: toolType.value,
editable: true,
path: path,
})
}
const overlaysInit = (e) => {
if (MapRef.value) {
MapRef.value.setFitView(e)
const queryBounds = (code) => {
_loading.value = true
if (!district) {
//DistrictSearch
const opts = {
subdistrict: 0,
extensions: 'all',
level: 'district'
};
district = new AMap.DistrictSearch(opts);
}
district.search(code, function (status, result) {
if (polygon) {
MapRef.value.remove(polygon)//
polygon = null;
}
const bounds = result?.districtList?.[0]?.boundaries;
if (bounds) {
//polygon
for (let i = 0; i < bounds.length; i += 1) {// MultiPolygonpath
bounds[i] = [bounds[i]]
}
polygon = new AMap.Polygon({
strokeWeight: 1,
path: bounds,
fillOpacity: 0.4,
fillColor: '#80d8ff',
strokeColor: '#0091ea'
});
MapRef.value.add(polygon)
MapRef.value.setFitView(polygon);//
}
_loading.value = false
});
}
const drawOverlays = (e) => {
overlay.type = 'create'
toolType.value = e
}
const toolDraw = (e) => {
if (toolType.value === 'circle') {
overlay.path = e.center
overlay.radius = e.radius
} else {
overlay.path = e
watch(() => [props.selectCode, loading.value], () => {
if (props.selectCode && !loading.value) {
queryBounds(String(props.selectCode).padEnd(6, '0'))
}
overlay.editable = true
overlay.type = toolType.value
setHistory({
type: toolType.value,
toolType: toolType.value,
editable: overlay.editable,
path: overlay.path,
radius: overlay.radius
})
}
const deleteOverlays = () => {
overlay.type = 'create'
overlay.editable = false
toolType.value = ''
setHistory({
type: 'create',
toolType: '',
editable: false,
path: [],
radius: 0
})
}
const cancel = () => {
if (historyList.value.length > 1) {
historyList.value.pop()
}
const oldData = historyList.value[historyList.value.length - 1]
if (oldData) {
overlay.type = oldData.type
overlay.editable = oldData.editable
overlay.path = oldData.path
overlay.radius = oldData.radius
toolType.value = oldData.toolType
}
}
}, {immediate: true})
</script>
@ -243,62 +75,17 @@ const cancel = () => {
position: relative;
height: 100%;
.map-tool{
.region-map-loading {
position: absolute;
top: 20%;
right: 20px;
.map-tool-content {
display: flex;
gap: 24px;
flex-direction: column;
.tool-item-group {
display: flex;
flex-direction: column;
border: 1px solid #e3e3e3;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 0 16px rgba(#000, .15);
.tool-item {
padding: 4px 6px;
color: #333;
font-size: 16px;
&:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
&:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
&:not(:first-child) {
border-top: 1px solid #e3e3e3;
}
&.active {
background-color: var(--ant-primary-color);
color: #fff;
}
&.disabled {
cursor: not-allowed !important;
background-color: #efefef;
> span {
cursor: not-allowed !important;
color: #666;
}
}
}
}
}
left: 0;
top: 0;
width: 100%;
background-color: yellow;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(#000, 0.35);
}
}
</style>

View File

@ -1,124 +1,138 @@
<template>
<j-tree-select
showSearch
placeholder="1级区域不需要选择"
:tree-data="builtInAreaList"
:value="_value"
:field-names="{
children: 'children',
<j-tree-select
showSearch
placeholder="1级区域不需要选择"
:tree-data="areaTree"
:value="_value"
:field-names="{
label: 'name',
value: 'code',
value: 'code'
}"
tree-node-filter-prop="name"
@select="onSelect"
>
<template #title="{ name, code }">
<span v-if="code">{{ name }} | {{ code }}</span>
</template>
</j-tree-select>
<j-checkbox
@change="onCheckChange"
v-model:checked="_checked"
style="margin-top: 5px"
>同步添加下一级区域</j-checkbox
>
tree-node-filter-prop="name"
@select="onSelect"
>
<template #title="{ name, code }">
<span v-if="code">{{ name }} | {{ code }}</span>
</template>
</j-tree-select>
<j-checkbox
v-model:checked="_checked"
@change="onCheckChange"
style="margin-top: 5px"
>同步添加下一级区域</j-checkbox
>
</template>
<script lang="ts" setup>
import { getBuiltinRegionTree } from '@/api/system/region';
import { onMounted, ref, watch } from 'vue';
import { ref, watch } from 'vue';
const props = defineProps({
value: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
children: {
type: Array,
default: () => [],
},
value: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
children: {
type: Array,
default: () => [],
},
areaTree: {
type: Array,
default: () => [],
},
});
const emits = defineEmits(['update:value', 'update:name', 'update:children']);
const features = ref<any>({});
const _value = ref<string>();
const builtInAreaList = ref<Record<string, any>[]>([]);
const _checked = ref<boolean>(false);
const _checked = ref<boolean>(props.children?.length ?? false);
const queryBuiltinRegionTree = async () => {
const resp = await getBuiltinRegionTree({
paging: false,
sorts: [{ name: 'sortIndex', order: 'asc' }],
});
if (resp.success) {
builtInAreaList.value = resp?.result || [];
const findChildren = (data: any, code: string) => {
let children: any[] = []
data.some((item: any) => {
if (item.code === code) {
children = item.children
return true
}
};
if (item.children) {
children = findChildren(item.children, code)
return !!children.length
}
return false
})
return children
}
const onCheckChange = (e: any) => {
if (e.target.checked) {
emits('update:children', features.value?.children || []);
} else {
emits('update:children', []);
}
console.log('e',props.children, e.target.checked)
if (e.target.checked) {
const children = features.value?.children ? features.value?.children : findChildren(props.areaTree, _value.value)
emits('update:children', children.map((item, index) => {
if (!item.sortIndex) {
item.sortIndex = index + 1
}
return item
}));
} else {
emits('update:children', []);
}
};
const getObj = (node: any): any => {
const _children = (node?.children || []).map((item: any) => {
return {
code: item.code,
name: item.name,
parentId: item.parentId,
};
});
const _children = (node?.children || []).map((item: any) => {
return {
code: node.code,
name: node.name,
parentId: node.parentId,
children: _children,
code: item.code,
name: item.name,
parentId: item.parentId,
};
});
return {
code: node.code,
name: node.name,
parentId: node.parentId,
children: _children,
};
};
const onSelect = (val: string, node: any) => {
features.value = getObj(node);
_value.value = val;
emits('update:name', features.value?.name);
emits('update:value', features.value?.code);
features.value = getObj(node);
_value.value = val;
emits('update:name', features.value?.name);
emits('update:value', features.value?.code);
if (_checked.value) {
emits('update:children', node?.children.map(item => ({
code: item.code,
name: item.name,
parentId: item.parentId,
})));
}
};
onMounted(() => {
queryBuiltinRegionTree();
});
watch(
() => props.value,
() => {
if (props.value) {
_value.value = props.value
} else {
emits('update:name', '中国');
emits('update:value', 100000);
}
if (props.value) {
_value.value = props.value
} else {
emits('update:name', '中国');
emits('update:value', 100000);
}
},
{
deep: true,
immediate: true,
},
);
watch(
() => props.children,
() => {
_checked.value = !!props.children?.length
},
{
deep: true,
immediate: true,
deep: true,
immediate: true,
},
);
</script>

View File

@ -1,45 +0,0 @@
<template>
<j-button @click="onClick" v-if="!_value.length" type="link"
>请在地图上描点</j-button
>
<div v-else>已完成描点<j-button type="link" @click="onEdit">编辑</j-button></div>
</template>
<script lang="ts" setup>
import { ref, watch, inject } from 'vue';
const props = defineProps({
value: {
type: Array,
default: () => [],
},
});
const emits = defineEmits(['update:value', 'close']);
const _value = ref<any[]>([]);
const __data: any = inject('system-region')
const onClick = () => {
console.log(__data)
__data.type.value = 'edit'
emits('close')
};
const onEdit = () => {
// __data.type.value = 'edit'
// emits('close')
}
watch(
() => props.value,
() => {
console.log(props.value)
},
{
deep: true,
immediate: true,
},
);
</script>

View File

@ -1,56 +1,44 @@
<template>
<j-modal
:maskClosable="false"
width="650px"
:visible="true"
:title="mode === 'edit' ? '编辑区域' : '新增区域'"
@ok="handleSave"
@cancel="handleCancel"
:confirmLoading="loading"
>
<div style="margin-top: 10px">
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
<j-form-item name="parentId" label="上级区域">
<j-tree-select
showSearch
v-model:value="modelRef.parentId"
placeholder="1级区域不需要选择"
:tree-data="areaList"
allowClear
:field-names="{
<j-modal
:maskClosable="false"
width="650px"
:visible="true"
:title="mode === 'edit' ? '编辑区域' : '新增区域'"
:confirmLoading="loading"
@ok="handleSave"
@cancel="() => { handleCancel() }"
>
<div style="margin-top: 10px">
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
<j-form-item name="parentId" label="上级区域">
<j-tree-select
showSearch
allowClear
v-model:value="modelRef.parentId"
placeholder="1级区域不需要选择"
:tree-data="areaList"
:field-names="{
children: 'children',
label: 'name',
value: 'id',
}"
tree-node-filter-prop="name"
/>
</j-form-item>
<j-form-item :name="['properties', 'type']" label="添加方式">
<j-radio-group
v-model:value="modelRef.properties.type"
button-style="solid"
@change="onChange"
>
<j-radio-button value="builtin"
>内置行政区</j-radio-button
>
<j-radio-button value="Custom"
>自定义数据</j-radio-button
>
</j-radio-group>
</j-form-item>
<j-form-item v-if="modelRef.properties.type === 'builtin'">
<BuildIn
v-model:value="modelRef.code"
v-model:children="modelRef.children"
v-model:name="modelRef.name"
/>
</j-form-item>
<j-form-item
label="区域名称"
name="name"
required
:rules="[
tree-node-filter-prop="name"
@change="treeSelect"
/>
</j-form-item>
<j-form-item label="内置行政区" v-if="modelRef.properties.type === 'builtin'">
<BuildIn
v-model:value="modelRef.code"
v-model:children="modelRef.children"
v-model:name="modelRef.name"
:areaTree="areaTree"
/>
</j-form-item>
<j-form-item
label="区域名称"
name="name"
required
:rules="[
{
required: true,
message: '请输入区域名称',
@ -64,17 +52,17 @@
trigger: 'blur',
},
]"
>
<j-input
v-model:value="modelRef.name"
placeholder="请输入区域名称"
/>
</j-form-item>
<j-form-item
label="行政区划代码"
name="code"
required
:rules="[
>
<j-input
v-model:value="modelRef.name"
placeholder="请输入区域名称"
/>
</j-form-item>
<j-form-item
label="行政区划代码"
name="code"
required
:rules="[
{
required: true,
message: '请输入行政区划代码',
@ -84,48 +72,50 @@
trigger: 'blur',
},
]"
>
<j-input-number
v-model:value="modelRef.code"
style="width: 100%"
placeholder="请输入行政区划代码"
/>
</j-form-item>
<j-form-item
v-if="modelRef.properties.type !== 'builtin'"
label="区划划分"
required
name="features"
>
<TracePoint @close="emit('close')" v-model:value="modelRef.features" />
</j-form-item>
</j-form>
</div>
</j-modal>
>
<j-input-number
v-model:value="modelRef.code"
style="width: 100%"
:disabled="modelRef.properties.type === 'builtin'"
placeholder="请输入行政区划代码"
/>
</j-form-item>
</j-form>
</div>
</j-modal>
</template>
<script lang="ts" setup>
import { ref, watch, reactive, PropType, onMounted } from 'vue';
import TracePoint from './TracePoint.vue';
<script lang="ts" setup name="Save">
import {ref, watch, reactive} from 'vue';
import type {PropType} from 'vue';
import BuildIn from './BuildIn.vue';
import {
validateName,
getRegionTree,
validateCode,
updateRegion,
validateName,
validateCode,
updateRegion,
} from '@/api/system/region';
import { onlyMessage } from '@/utils/comm';
import {omit} from "lodash-es";
import {onlyMessage} from "@/utils/comm";
const emit = defineEmits(['close', 'save']);
const props = defineProps({
data: {
type: Object,
default: () => {},
},
mode: {
type: String as PropType<'add' | 'edit'>,
default: 'add',
data: {
type: Object,
default: () => {
},
},
treeData: {
type: Array,
default: () => []
},
areaTree: {
type: Array,
default: () => []
},
mode: {
type: String as PropType<'add' | 'edit'>,
default: 'add',
},
});
const areaList = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false);
@ -133,15 +123,16 @@ const loading = ref<boolean>(false);
const formRef = ref();
const init = {
parentId: undefined,
id: undefined,
name: undefined,
code: undefined,
features: undefined,
children: [],
properties: {
type: 'builtin',
},
parentId: undefined,
id: undefined,
name: undefined,
code: undefined,
features: undefined,
children: [],
properties: {
type: 'builtin',
},
sortIndex: props.data.sortIndex || 1
};
const modelRef = reactive(init);
@ -149,86 +140,121 @@ const modelRef = reactive(init);
watch(
() => props.data,
() => {
Object.assign(modelRef, {});
if (props.mode === 'add' && props.data?.id) {
//
Object.assign(modelRef, {
...init,
parentId: props.data.id,
});
} else if (props.mode === 'edit') {
//
Object.assign(modelRef, props.data);
} else {
Object.assign(modelRef, init);
}
Object.assign(modelRef, {});
if (props.mode === 'add') {
//
Object.assign(modelRef, {
...init,
...props.data,
});
} else if (props.mode === 'edit') {
//
Object.assign(modelRef, props.data);
} else {
Object.assign(modelRef, init);
}
},
{ immediate: true, deep: true },
{immediate: true, deep: true},
);
const handleCancel = () => {
emit('close');
const handleCancel = (data: any) => {
emit('close', data);
};
const traceEdit = () => {
const newData: any = {
...props.data,
...modelRef,
}
handleCancel(newData)
}
const treeSelect = (id: string, label: string, extra: any) => {
let children: any[]
if (extra) {
children = extra?.triggerNode?.props.children || []
} else {
children = props.treeData
}
const lastItem = children.length ? children[children.length-1] : {}
modelRef.sortIndex = lastItem.sortIndex ? lastItem.sortIndex + 1 : 1
}
const handleSave = () => {
formRef.value
.validate()
.then(async (_data: any) => {
loading.value = true;
const resp = await updateRegion({
...props.data,
...modelRef,
}).finally(() => {
loading.value = false;
});
if (resp.status === 200) {
onlyMessage('操作成功!');
emit('save');
formRef.value
.validate()
.then(async (_data: any) => {
const newData: any = {
...omit(props.data, ['parentFullName', 'parentId']),
...modelRef,
}
newData.fullName = props.data.parentFullName ? props.data.parentFullName + modelRef.name : modelRef.name
newData.parentId = newData.parentId || ''
const arr = areaList.value.map(item=>item.code)
if (newData.children?.length) {
newData.children = newData.children.map(item => {
if (!item.fullName) {
item.fullName = newData.fullName + item.name
}
})
.catch((err: any) => {
console.log('error', err);
item = {
...item,
children: []
}
return item
}).filter(item =>!arr.includes(item.code))
}
loading.value = true;
const resp = await updateRegion(newData).finally(() => {
loading.value = false;
});
};
if (resp.status === 200) {
onlyMessage('操作成功!');
emit('save');
}
})
.catch((err: any) => {
console.log('error', err);
});
}
const vailName = async (_: Record<string, any>, value: string) => {
if (!props?.data?.id && value) {
const resp = await validateName(value, props.data.id);
if (resp.status === 200 && !resp.result?.passed) {
return Promise.reject(resp.result?.reason || '区域名称重复');
} else {
return Promise.resolve();
}
if (!props?.data?.id && value) {
const resp = await validateName(value, props.data.id);
if (resp.status === 200 && !resp.result?.passed) {
return Promise.reject('区域名称重复');
} else {
return Promise.resolve();
return Promise.resolve();
}
} else {
return Promise.resolve();
}
};
const vailCode = async (_: Record<string, any>, value: string) => {
if (!props?.data?.id && value) {
const resp = await validateCode(value, props.data.id);
if (resp.status === 200 && !resp.result?.passed) {
return Promise.reject(resp.result?.reason || '行政区域代码重复');
} else {
return Promise.resolve();
}
if (!props?.data?.id && value) {
const resp = await validateCode(value, props.data.id);
if (resp.status === 200 && !resp.result?.passed) {
return Promise.reject('行政区域代码重复');
} else {
return Promise.resolve();
return Promise.resolve();
}
} else {
return Promise.resolve();
}
};
const onChange = () => {
modelRef.features = undefined;
modelRef.features = undefined;
};
const handleSearch = async () => {
const resp = await getRegionTree();
if (resp.success) {
areaList.value = resp?.result || [];
}
};
onMounted(() => {
handleSearch();
});
watch(() => JSON.stringify(props.treeData), () => {
const item = JSON.parse(JSON.stringify(props.treeData))
areaList.value = item
if(props.mode === 'add'){
modelRef.children = props.areaTree?.[0]?.children
}
}, {immediate: true})
</script>

View File

@ -0,0 +1,51 @@
import { getBuiltinRegionTree } from '@/api/system/region';
import {useRequest} from "@/hook";
export const useArea = () => {
const areaMap = ref(new Map())
const handleAreaMap = (data: any[] = [], parentCode?: string) => {
for (let i = 0;i < data.length; i++) {
const _data = data[i]
areaMap.value.set(_data.code, {
parentId: parentCode,
name: _data.name
})
if (_data.children?.length) {
handleAreaMap(_data.children, _data.code)
}
}
}
const { data: areaTree } = useRequest(getBuiltinRegionTree, {
onSuccess(resp) {
handleAreaMap(resp?.result)
},
defaultParams: [{
paging: false,
sorts: [{ name: 'sortIndex', order: 'asc' }],
}]
})
const getParentNameById = (id?: string) => {
let _id = id
const name = []
while (!!_id) {
const item = areaMap.value.get(_id)
if (item) { // 不添加本身
name.unshift(item.name)
}
_id = item?.parentId
}
return name
}
return {
areaTree,
areaMap,
getParentNameById
}
}

View File

@ -1,13 +1,12 @@
<template>
<page-container>
<full-page>
<full-page fixed>
<div class="region">
<div class="left">
<LeftTree />
<div class="mask" v-if="type === 'edit'"></div>
<LeftTree @select="onSelect" />
</div>
<div class="right">
<Map :path="path" :type="mapType" />
<Map ref="mapRef" :selectCode="selectCode" />
</div>
</div>
</full-page>
@ -17,18 +16,13 @@
<script setup lang="ts" name="RegionMange">
import LeftTree from './LeftTree/index.vue'
import Map from './MapTool/map.vue'
import FullPage from "components/Layout/FullPage.vue";
import { provide } from 'vue'
import FullPage from "@/components/Layout/FullPage.vue";
const path = ref([[121.5273285, 31.21515044], [121.5293285, 31.21515044], [121.5293285, 31.21915044], [121.5273285, 31.21515044]])
const type = ref<'view' | 'edit'>('view')
const mapType = ref<string>('create')
const selectCode = ref('')
provide('system-region', {
type,
mapType: '',
path
})
const onSelect = (dt: string) => {
selectCode.value = dt
}
</script>
<style lang="less" scoped>

View File

@ -54,10 +54,10 @@ import { useMenuStore } from '@/store/menu';
// import { USER_CENTER_MENU_DATA } from '@/views/init-home/data/baseMenu'
import {
getRoleDetails_api,
updateRole_api,
editRole_api,
updatePrimissTree_api,
getRoleDetails_api,
updateRole_api,
editRole_api,
updatePrimissTree_api, clearPrimissTree_api,
} from '@/api/system/role';
import { onlyMessage } from '@/utils/comm';
@ -85,13 +85,21 @@ const form = reactive({
clickSave: () => {
// formRef.value?.validate().then(() => {
// const updateRole = editRole_api(roleId, form.data);
if(form.menus?.length > 0){
const updateTree = updatePrimissTree_api(roleId, {
menus: form.menus,
menus: form.menus,
});
Promise.all([ updateTree]).then((resp) => {
onlyMessage('操作成功');
// jumpPage(`system/Role`);
onlyMessage('操作成功');
// jumpPage(`system/Role`);
})
} else {
clearPrimissTree_api(roleId).then(resp => {
if(resp.success){
onlyMessage('操作成功');
}
})
}
// });
},
});

View File

@ -51,6 +51,7 @@
>
<PermissionButton
type="text"
style="padding: 0"
hasPermission="system/Role:groupDelete"
:popConfirm="{
title: `确定要删除?`,

View File

@ -14,7 +14,10 @@
model="TABLE"
:params="queryParams"
:defaultParams="{
sorts: [{ name: 'createTime', order: 'desc' }],
sorts: [
{ name: 'createTime', order: 'desc' },
{ name: 'username', order: 'asc', value: 'admin' },
],
}"
>
<template #headerTitle>
@ -31,9 +34,13 @@
</template>
<template #roleList="slotProps">
<j-ellipsis>
{{ slotProps?.roleList?.map((item)=>{
return item.name
}).join(',') }}
{{
slotProps?.roleList
?.map((item) => {
return item.name;
})
.join(',')
}}
</j-ellipsis>
</template>
<template #status="slotProps">
@ -101,11 +108,10 @@
: '删除',
}"
:popConfirm="{
title:'确认删除?',
title: '确认删除?',
onConfirm: () =>
table.clickDel(slotProps.id),
}"
:disabled="slotProps.status"
>
<AIcon type="DeleteOutlined" />
@ -134,7 +140,7 @@ import {
getUserList_api,
changeUserStatus_api,
deleteUser_api,
queryRole_api
queryRole_api,
} from '@/api/system/user';
import { onlyMessage } from '@/utils/comm';
@ -184,30 +190,28 @@ const columns = [
title: '角色',
dataIndex: 'roleList',
key: 'roleList',
search:{
type:'select',
search: {
type: 'select',
// rename:'id$in-dimension$role',
options:() =>
new Promise((resolve)=>{
queryRole_api(
{
paging:false,
options: () =>
new Promise((resolve) => {
queryRole_api({
paging: false,
sorts: [
{ name: 'createTime', order: 'desc' },
{ name: 'id', order: 'desc' },
]
}
).then((resp:any)=>{
resolve(
],
}).then((resp: any) => {
resolve(
resp.result.map((item: dictType) => ({
label: item.name,
value: item.id,
})),
);
})
})
});
}),
},
scopedSlots: true,
scopedSlots: true,
},
{
title: '状态',
@ -304,13 +308,15 @@ type modalType = '' | 'add' | 'edit' | 'reset';
const handleParams = (params: any) => {
const newParams = (params?.terms as any[])?.map((termsGroupA) => {
let arr: any[] = []
let arr: any[] = [];
termsGroupA.terms = termsGroupA.terms.map((termsItem: any) => {
if (termsItem.column === 'id$in-dimension$role') {
let _termType = termsItem.termType === 'nin' ? 'not$in' : termsItem.termType
termsItem.column = `${termsItem.column}$${_termType}`
delete termsItem.termType
let _termType =
termsItem.termType === 'nin'
? 'not$in'
: termsItem.termType;
termsItem.column = `${termsItem.column}$${_termType}`;
delete termsItem.termType;
}
if (['telephone', 'email'].includes(termsItem.column)) {
return {
@ -318,7 +324,10 @@ const handleParams = (params: any) => {
value: [termsItem],
};
}
if (['type'].includes(termsItem.column) && termsItem.value === 'other') {
if (
['type'].includes(termsItem.column) &&
termsItem.value === 'other'
) {
arr = [
{
...termsItem,
@ -331,8 +340,26 @@ const handleParams = (params: any) => {
type: 'or',
termType: 'empty',
value: 1,
}
]
},
];
}
if (termsItem.column === 'roleList') {
if (
termsItem.termType === 'eq' ||
termsItem.termType === 'in'
) {
return {
column: 'id$in-dimension$role',
type: termsItem.type,
value: termsItem.value,
};
} else {
return {
column: 'id$in-dimension$role$not',
type: termsItem.type,
value: termsItem.value,
};
}
}
if(termsItem.column === 'roleList'){
if(termsItem.termType === 'eq' || termsItem.termType === 'in'){
@ -352,8 +379,8 @@ const handleParams = (params: any) => {
return termsItem;
});
if(arr.length){
termsGroupA.terms = [...termsGroupA.terms, ...arr]
if (arr.length) {
termsGroupA.terms = [...termsGroupA.terms, ...arr];
}
return termsGroupA;

View File

@ -98,7 +98,7 @@ export default defineConfig(({ mode}) => {
// target: 'http://120.77.179.54:8844', // 120测试
target: 'http://192.168.33.46:8844', // 本地开发环境
// target: 'http://192.168.32.167:8844', // 本地开发环境1
// target: 'http://192.168.33.1:8848', // 社区版开发环境
// target: 'http://192.168.33.6:8848', // 社区版开发环境
// target: 'http://192.168.32.207:8844', // 刘本地
// target: 'http://192.168.32.187:8844', // 谭本地
// target: 'http://192.168.33.66:8844', // 苟本地