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 #!/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 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); export const queryHistoryList = (data:any) => server.post('/alarm/history/_query',data);
/** /**
* *
*/ */

View File

@ -1,7 +1,7 @@
import server from '@/utils/request'; 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}` : ''}`); 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 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 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); 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 { getAMapUiPromise } from './utils';
import { useSystem } from '@/store/system'; import { useSystem } from '@/store/system';
const emit = defineEmits('init') const emit = defineEmits(['init'])
const system = useSystem(); const system = useSystem();
interface AMapProps { interface AMapProps {
@ -33,7 +33,9 @@ interface AMapProps {
const amapKey = system.$state.configInfo.amap?.apiKey; const amapKey = system.$state.configInfo.amap?.apiKey;
initAMapApiLoader({ initAMapApiLoader({
key: amapKey || '', key: amapKey || 'c86c1b22c6b223e3ed08815532676445',
securityJsCode: 'b0efcf1ce14cbf2d56d3cde630cd19cf',
plugins: ['AMap.DistrictSearch'],
}); });
const props = defineProps({ const props = defineProps({

View File

@ -23,4 +23,4 @@ export const getAMapUiPromise = (version: string = '1.0'): Promise<any> => {
document.body.append(script); document.body.append(script);
return pro; return pro;
}; };

View File

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

View File

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

View File

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

View File

@ -12,26 +12,36 @@
</PermissionButton> </PermissionButton>
</template> </template>
<j-descriptions-item label="设备ID"> <j-descriptions-item label="设备ID">
<div style="display: flex"> <div style="display: flex">
<div style="flex: 1"> <div style="flex: 1">
<j-ellipsis> {{ instanceStore.current?.id }} </j-ellipsis> <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>
<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>
<j-descriptions-item label="产品名称">{{ <j-descriptions-item label="产品名称">{{
instanceStore.current?.productName instanceStore.current?.productName
@ -51,29 +61,27 @@
<j-descriptions-item label="创建时间">{{ <j-descriptions-item label="创建时间">{{
instanceStore.current?.createTime instanceStore.current?.createTime
? moment(instanceStore.current?.createTime).format( ? moment(instanceStore.current?.createTime).format(
'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss',
) )
: '' : ''
}}</j-descriptions-item> }}</j-descriptions-item>
<j-descriptions-item label="注册时间">{{ <j-descriptions-item label="注册时间">{{
instanceStore.current?.registerTime instanceStore.current?.registerTime
? moment(instanceStore.current?.registerTime).format( ? moment(instanceStore.current?.registerTime).format(
'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss',
) )
: '' : ''
}}</j-descriptions-item> }}</j-descriptions-item>
<j-descriptions-item label="最后上线时间">{{ <j-descriptions-item label="最后上线时间">{{
instanceStore.current?.onlineTime instanceStore.current?.onlineTime
? moment(instanceStore.current?.onlineTime).format( ? moment(instanceStore.current?.onlineTime).format(
'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss',
) )
: '' : ''
}}</j-descriptions-item> }}</j-descriptions-item>
<j-descriptions-item <j-descriptions-item
label="父设备" label="父设备"
v-if=" v-if="instanceStore.current?.deviceType?.value === 'childrenDevice'"
instanceStore.current?.deviceType?.value === 'childrenDevice'
"
>{{ instanceStore.current?.parentId }}</j-descriptions-item >{{ instanceStore.current?.parentId }}</j-descriptions-item
> >
<j-descriptions-item label="说明">{{ <j-descriptions-item label="说明">{{
@ -99,14 +107,14 @@
@close="visible = false" @close="visible = false"
@save="saveBtn" @save="saveBtn"
/> />
<InkingModal <InkingModal
v-if='inkingVisible' v-if="inkingVisible"
:id='inklingDeviceId' :id="inklingDeviceId"
:accessId='instanceStore.current.accessId' :accessId="instanceStore.current.accessId"
:pluginId="channelId" :pluginId="channelId"
@cancel="inkingVisible = false" @cancel="inkingVisible = false"
@submit='saveInkling' @submit="saveInkling"
/> />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -115,16 +123,16 @@ import Save from '../../Save/index.vue';
import Config from './components/Config/index.vue'; import Config from './components/Config/index.vue';
import Tags from './components/Tags/index.vue'; import Tags from './components/Tags/index.vue';
import Relation from './components/Relation/index.vue'; import Relation from './components/Relation/index.vue';
import InkingModal from './components/InklingModal' import InkingModal from './components/InklingModal';
import moment from 'moment'; import moment from 'moment';
import { detail as queryPluginAccessDetail } from '@/api/link/accessConfig' import { detail as queryPluginAccessDetail } from '@/api/link/accessConfig';
import { getPluginData } from '@/api/link/plugin' import { getPluginData } from '@/api/link/plugin';
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
const inkingVisible = ref<boolean>(false); const inkingVisible = ref<boolean>(false);
const instanceStore = useInstanceStore(); const instanceStore = useInstanceStore();
const inklingDeviceId = ref() const inklingDeviceId = ref();
const channelId = ref() const channelId = ref();
const saveBtn = () => { const saveBtn = () => {
if (instanceStore.current?.id) { if (instanceStore.current?.id) {
@ -134,36 +142,51 @@ const saveBtn = () => {
}; };
const saveInkling = (id: string) => { const saveInkling = (id: string) => {
if (instanceStore.current?.id) { if (instanceStore.current?.id) {
instanceStore.refresh(instanceStore.current?.id); instanceStore.refresh(instanceStore.current?.id);
} }
channelId.value = id channelId.value = id;
queryInkling() queryInkling();
inkingVisible.value = false inkingVisible.value = false;
} };
const giveAnInkling = () => { const giveAnInkling = () => {
inkingVisible.value = true inkingVisible.value = true;
} };
const queryInkling = () => { const queryInkling = () => {
if (instanceStore.current?.accessProvider === 'plugin_gateway') { if (instanceStore.current?.accessProvider === 'plugin_gateway') {
queryPluginAccessDetail(instanceStore.current?.accessId).then(async res => { queryPluginAccessDetail(instanceStore.current?.accessId).then(
if (res.success) { async (res) => {
channelId.value = res.result.channelId if (res.success) {
const pluginRes = await getPluginData('device',instanceStore.current?.accessId, instanceStore.current?.id) channelId.value = res.result.channelId;
if (pluginRes.success) { const pluginRes = await getPluginData(
inklingDeviceId.value = pluginRes.result?.externalId '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();
} }
} },
}) { immediate: true },
} );
}
watch(() => instanceStore.current?.id, () => {
if (instanceStore.current?.id) {
queryInkling()
}
}, { immediate: true })
</script> </script>

View File

@ -638,6 +638,7 @@ const getGuide = async (isDriver1: boolean = false) => {
}; };
const checkAccess = async (data: any) => { const checkAccess = async (data: any) => {
console.log(data)
visible.value = false visible.value = false
accessId.value = data.access.id accessId.value = data.access.id
access.value = data.access access.value = data.access
@ -648,6 +649,20 @@ const checkAccess = async (data: any) => {
metadata.value = data.metadata?.[0] || { metadata.value = data.metadata?.[0] || {
properties: [] 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') { // if (data.access.channel === 'plugin') { //
markdownToHtml.value = '' markdownToHtml.value = ''
productTypes.value = data.productTypes.map(item => ({ ...item, label: item.name, value: item.id})) productTypes.value = data.productTypes.map(item => ({ ...item, label: item.name, value: item.id}))

View File

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

View File

@ -892,6 +892,17 @@ const handleImport = async () => {
// _object as DeviceMetadata, // _object as DeviceMetadata,
// ); // );
// console.log(copyOperateLimits,_object); // // 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 = { const params = {
id, id,
metadata: JSON.stringify(_object), 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)=>{ export const testType = (data:any,index:number,isArray?:boolean,isObject?:boolean)=>{
console.log(data,index)
if(data.type === 'boolean'){ if(data.type === 'boolean'){
if(!data?.trueText || !data?.trueValue || !data?.falseText || !data?.falseValue){ if(!data?.trueText || !data?.trueValue || !data?.falseText || !data?.falseValue){
onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失必填属性`,'error') 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){ if(data.type === 'object' && !isArray && !isObject){
console.log(data,'data123')
if(data?.properties?.length > 0){ if(data?.properties?.length > 0){
return testObject(data.properties,index) return testObject(data.properties,index)
}else{ }else{

View File

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

View File

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

View File

@ -575,10 +575,12 @@ const columns = [
search: { search: {
type: 'select', type: 'select',
options: [ options: [
{ label: '未同步', value: 'notReady' },
{ label: '同步失败', value: 'error' },
{ label: '激活', value: 'using' }, { label: '激活', value: 'using' },
{ label: '未激活', value: 'toBeActivated' }, { label: '未激活', value: 'toBeActivated' },
{ label: '停机', value: 'deactivate' }, { label: '停机', value: 'deactivate' },
{ label: '其它', value: 'using,toBeActivated,deactivate' }, { label: '其它', value: 'other' },
], ],
}, },
}, },
@ -759,20 +761,7 @@ const getActions = (
}; };
const handleSearch = (e: any) => { const handleSearch = (e: any) => {
const newParams = (e?.terms as any[])?.map((item1) => { params.value = { terms: e?.terms || [] };
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 || [] };
}; };
const onSelectChange = (keys: string[], rows: []) => { const onSelectChange = (keys: string[], rows: []) => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -120,13 +120,17 @@
{{ titleMap.get(slotProps.targetType) }} {{ titleMap.get(slotProps.targetType) }}
</template> </template>
<template #alarmTime="slotProps"> <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>
<template #level="slotProps"> <template #level="slotProps">
<Ellipsis style="width: calc(100% - 20px)"> <Ellipsis style="width: calc(100% - 20px)">
{{ data.defaultLevel.find((i)=>{ {{
return i.level === slotProps.level data.defaultLevel.find((i) => {
}).title}} return i.level === slotProps.level;
}).title
}}
</Ellipsis> </Ellipsis>
</template> </template>
<template #state="slotProps"> <template #state="slotProps">
@ -229,26 +233,26 @@ titleMap.set('other', '其他');
titleMap.set('org', '组织'); titleMap.set('org', '组织');
const columns = [ const columns = [
{ {
title:'配置名称', title: '配置名称',
dataIndex:'alarmName', dataIndex: 'alarmName',
key:'alarmName', key: 'alarmName',
},{
title:'类型',
dataIndex:'targetType',
key:'targetType',
scopedSlots:true
}, },
{ {
title:'关联场景联动', title: '类型',
dataIndex:'sourceName', dataIndex: 'targetType',
key:'sourceName', key: 'targetType',
scopedSlots: true,
},
{
title: '关联场景联动',
dataIndex: 'sourceName',
key: 'sourceName',
}, },
{ {
title: '告警级别', title: '告警级别',
dataIndex: 'level', dataIndex: 'level',
key: 'level', key: 'level',
width:200, width: 200,
search: { search: {
type: 'select', type: 'select',
options: data.value.defaultLevel.map((item: any) => { options: data.value.defaultLevel.map((item: any) => {
@ -258,7 +262,7 @@ const columns = [
}; };
}), }),
}, },
scopedSlots: true scopedSlots: true,
}, },
{ {
title: '最近告警时间', title: '最近告警时间',
@ -300,49 +304,52 @@ const columns = [
const newColumns = computed(() => { const newColumns = computed(() => {
const otherColumns = { const otherColumns = {
title: '产品名称', title: '产品名称',
dataIndex: 'targetId', dataIndex: 'targetName',
key: 'targetId', 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: { search: {
type: 'select', type: 'string',
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 [];
},
}, },
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -108,10 +108,12 @@
tooltip="填写访问其它平台的地址" tooltip="填写访问其它平台的地址"
/> />
</template> </template>
<j-input
<!-- <j-input
v-model:value="form.data.page.baseUrl" v-model:value="form.data.page.baseUrl"
placeholder="请输入接入地址" placeholder="请输入接入地址"
/> /> -->
<InputGroup v-model:value="form.data.page.baseUrl" style="width: 100%;"/>
</j-form-item> </j-form-item>
<j-form-item <j-form-item
label="路由方式" label="路由方式"
@ -1436,6 +1438,7 @@ import { cloneDeep, difference } from 'lodash-es';
import { useMenuStore } from '@/store/menu'; import { useMenuStore } from '@/store/menu';
import { Rule } from 'ant-design-vue/lib/form'; import { Rule } from 'ant-design-vue/lib/form';
import ApplyList from './ApplyList/index.vue'; import ApplyList from './ApplyList/index.vue';
import InputGroup from './InputGroup.vue'
const emit = defineEmits(['changeApplyType']); 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 visible = ref<boolean>(false);
const addMenuVisible = 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 = [ const columns = [
{ {
title: '名称', title: '名称',
@ -272,17 +261,17 @@ const columns = [
search: { search: {
type: 'select', type: 'select',
options: typeOptions, options: typeOptions,
// options: () => options: () =>
// new Promise((resolve) => { new Promise((resolve) => {
// queryType().then((resp: any) => { queryType().then((resp: any) => {
// resolve( resolve(
// resp.result.map((item: any) => ({ resp.result.map((item: any) => ({
// label: item.name, label: item.name,
// value: item.provider, value: item.provider,
// })), })),
// ); );
// }); });
// }), }),
}, },
scopedSlots: true, scopedSlots: true,
}, },

View File

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

View File

@ -1,5 +1,5 @@
<template> <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="detail">
<div class="item"> <div class="item">
<div class="label">通知方式</div> <div class="label">通知方式</div>

View File

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

View File

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

View File

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

View File

@ -1,240 +1,72 @@
<template> <template>
<div class="region-map"> <div class="region-map">
<AMapComponent <AMapComponent @init="initMap"/>
@init="initMap" <div class="region-map-loading" v-if="_loading || loading">
> <a-spin :spinning="_loading || loading"></a-spin>
<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>
</div> </div>
</div> </div>
</template> </template>
<script name="RegionMap" setup> <script name="RegionMap" setup>
const props = defineProps({ const props = defineProps({
path: { selectCode: {
type: Array,
default: () => []
},
radius: {
type: Number,
default: 0
},
type: {
type: String, type: String,
default: 'polygon' default: undefined
}, },
}) })
const emit = defineEmits('dragend')
const MapRef = ref() const MapRef = ref()
const toolType = ref('') const loading = ref(true)
const historyList = ref([]) const _loading = ref(true)
let polygon = null
const overlay = reactive({ let district = null
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 initMap = (e) => { const initMap = (e) => {
loading.value = true
MapRef.value = e MapRef.value = e
loading.value = false
} }
const polygonDraw = (e) => { const queryBounds = (code) => {
_loading.value = true
const target = e.target if (!district) {
const path = target.getPath() //DistrictSearch
setHistory({ const opts = {
type: 'polygon', subdistrict: 0,
toolType: toolType.value, extensions: 'all',
editable: true, level: 'district'
path: path.map(item => [item.lng, item.lat]), };
}) district = new AMap.DistrictSearch(opts);
}
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)
} }
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) => { watch(() => [props.selectCode, loading.value], () => {
overlay.type = 'create' if (props.selectCode && !loading.value) {
toolType.value = e queryBounds(String(props.selectCode).padEnd(6, '0'))
}
const toolDraw = (e) => {
if (toolType.value === 'circle') {
overlay.path = e.center
overlay.radius = e.radius
} else {
overlay.path = e
} }
overlay.editable = true }, {immediate: 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
}
}
</script> </script>
@ -243,62 +75,17 @@ const cancel = () => {
position: relative; position: relative;
height: 100%; height: 100%;
.map-tool{ .region-map-loading {
position: absolute; position: absolute;
top: 20%; left: 0;
right: 20px; top: 0;
width: 100%;
background-color: yellow;
.map-tool-content { height: 100%;
display: flex; display: flex;
gap: 24px; justify-content: center;
flex-direction: column; align-items: center;
background-color: rgba(#000, 0.35);
.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;
}
}
}
}
}
} }
} }
</style> </style>

View File

@ -1,124 +1,138 @@
<template> <template>
<j-tree-select <j-tree-select
showSearch showSearch
placeholder="1级区域不需要选择" placeholder="1级区域不需要选择"
:tree-data="builtInAreaList" :tree-data="areaTree"
:value="_value" :value="_value"
:field-names="{ :field-names="{
children: 'children',
label: 'name', label: 'name',
value: 'code', value: 'code'
}" }"
tree-node-filter-prop="name" tree-node-filter-prop="name"
@select="onSelect" @select="onSelect"
> >
<template #title="{ name, code }"> <template #title="{ name, code }">
<span v-if="code">{{ name }} | {{ code }}</span> <span v-if="code">{{ name }} | {{ code }}</span>
</template> </template>
</j-tree-select> </j-tree-select>
<j-checkbox <j-checkbox
@change="onCheckChange" v-model:checked="_checked"
v-model:checked="_checked" @change="onCheckChange"
style="margin-top: 5px" style="margin-top: 5px"
>同步添加下一级区域</j-checkbox >同步添加下一级区域</j-checkbox
> >
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { getBuiltinRegionTree } from '@/api/system/region'; import { ref, watch } from 'vue';
import { onMounted, ref, watch } from 'vue';
const props = defineProps({ const props = defineProps({
value: { value: {
type: String, type: String,
default: '', default: '',
}, },
name: { name: {
type: String, type: String,
default: '', default: '',
}, },
children: { children: {
type: Array, type: Array,
default: () => [], default: () => [],
}, },
areaTree: {
type: Array,
default: () => [],
},
}); });
const emits = defineEmits(['update:value', 'update:name', 'update:children']); const emits = defineEmits(['update:value', 'update:name', 'update:children']);
const features = ref<any>({}); const features = ref<any>({});
const _value = ref<string>(); const _value = ref<string>();
const builtInAreaList = ref<Record<string, any>[]>([]); const _checked = ref<boolean>(props.children?.length ?? false);
const _checked = ref<boolean>(false);
const queryBuiltinRegionTree = async () => {
const resp = await getBuiltinRegionTree({ const findChildren = (data: any, code: string) => {
paging: false, let children: any[] = []
sorts: [{ name: 'sortIndex', order: 'asc' }],
}); data.some((item: any) => {
if (resp.success) { if (item.code === code) {
builtInAreaList.value = resp?.result || []; children = item.children
return true
} }
};
if (item.children) {
children = findChildren(item.children, code)
return !!children.length
}
return false
})
return children
}
const onCheckChange = (e: any) => { const onCheckChange = (e: any) => {
if (e.target.checked) { console.log('e',props.children, e.target.checked)
emits('update:children', features.value?.children || []); if (e.target.checked) {
} else { const children = features.value?.children ? features.value?.children : findChildren(props.areaTree, _value.value)
emits('update:children', []); 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 getObj = (node: any): any => {
const _children = (node?.children || []).map((item: any) => { const _children = (node?.children || []).map((item: any) => {
return {
code: item.code,
name: item.name,
parentId: item.parentId,
};
});
return { return {
code: node.code, code: item.code,
name: node.name, name: item.name,
parentId: node.parentId, parentId: item.parentId,
children: _children,
}; };
});
return {
code: node.code,
name: node.name,
parentId: node.parentId,
children: _children,
};
}; };
const onSelect = (val: string, node: any) => { const onSelect = (val: string, node: any) => {
features.value = getObj(node); features.value = getObj(node);
_value.value = val; _value.value = val;
emits('update:name', features.value?.name);
emits('update:value', features.value?.code); 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( watch(
() => props.value, () => props.value,
() => { () => {
if (props.value) { if (props.value) {
_value.value = props.value _value.value = props.value
} else { } else {
emits('update:name', '中国'); emits('update:name', '中国');
emits('update:value', 100000); emits('update:value', 100000);
} }
}, },
{ {
deep: true, deep: true,
immediate: true, immediate: true,
}, },
); );
</script>
watch(
() => props.children,
() => {
_checked.value = !!props.children?.length
},
{
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> <template>
<j-modal <j-modal
:maskClosable="false" :maskClosable="false"
width="650px" width="650px"
:visible="true" :visible="true"
:title="mode === 'edit' ? '编辑区域' : '新增区域'" :title="mode === 'edit' ? '编辑区域' : '新增区域'"
@ok="handleSave" :confirmLoading="loading"
@cancel="handleCancel" @ok="handleSave"
:confirmLoading="loading" @cancel="() => { handleCancel() }"
> >
<div style="margin-top: 10px"> <div style="margin-top: 10px">
<j-form :layout="'vertical'" ref="formRef" :model="modelRef"> <j-form :layout="'vertical'" ref="formRef" :model="modelRef">
<j-form-item name="parentId" label="上级区域"> <j-form-item name="parentId" label="上级区域">
<j-tree-select <j-tree-select
showSearch showSearch
v-model:value="modelRef.parentId" allowClear
placeholder="1级区域不需要选择" v-model:value="modelRef.parentId"
:tree-data="areaList" placeholder="1级区域不需要选择"
allowClear :tree-data="areaList"
:field-names="{ :field-names="{
children: 'children', children: 'children',
label: 'name', label: 'name',
value: 'id', value: 'id',
}" }"
tree-node-filter-prop="name" tree-node-filter-prop="name"
/> @change="treeSelect"
</j-form-item> />
<j-form-item :name="['properties', 'type']" label="添加方式"> </j-form-item>
<j-radio-group <j-form-item label="内置行政区" v-if="modelRef.properties.type === 'builtin'">
v-model:value="modelRef.properties.type" <BuildIn
button-style="solid" v-model:value="modelRef.code"
@change="onChange" v-model:children="modelRef.children"
> v-model:name="modelRef.name"
<j-radio-button value="builtin" :areaTree="areaTree"
>内置行政区</j-radio-button />
> </j-form-item>
<j-radio-button value="Custom" <j-form-item
>自定义数据</j-radio-button label="区域名称"
> name="name"
</j-radio-group> required
</j-form-item> :rules="[
<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="[
{ {
required: true, required: true,
message: '请输入区域名称', message: '请输入区域名称',
@ -64,17 +52,17 @@
trigger: 'blur', trigger: 'blur',
}, },
]" ]"
> >
<j-input <j-input
v-model:value="modelRef.name" v-model:value="modelRef.name"
placeholder="请输入区域名称" placeholder="请输入区域名称"
/> />
</j-form-item> </j-form-item>
<j-form-item <j-form-item
label="行政区划代码" label="行政区划代码"
name="code" name="code"
required required
:rules="[ :rules="[
{ {
required: true, required: true,
message: '请输入行政区划代码', message: '请输入行政区划代码',
@ -84,48 +72,50 @@
trigger: 'blur', trigger: 'blur',
}, },
]" ]"
> >
<j-input-number <j-input-number
v-model:value="modelRef.code" v-model:value="modelRef.code"
style="width: 100%" style="width: 100%"
placeholder="请输入行政区划代码" :disabled="modelRef.properties.type === 'builtin'"
/> placeholder="请输入行政区划代码"
</j-form-item> />
<j-form-item </j-form-item>
v-if="modelRef.properties.type !== 'builtin'" </j-form>
label="区划划分" </div>
required </j-modal>
name="features"
>
<TracePoint @close="emit('close')" v-model:value="modelRef.features" />
</j-form-item>
</j-form>
</div>
</j-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup name="Save">
import { ref, watch, reactive, PropType, onMounted } from 'vue'; import {ref, watch, reactive} from 'vue';
import TracePoint from './TracePoint.vue'; import type {PropType} from 'vue';
import BuildIn from './BuildIn.vue'; import BuildIn from './BuildIn.vue';
import { import {
validateName, validateName,
getRegionTree, validateCode,
validateCode, updateRegion,
updateRegion,
} from '@/api/system/region'; } 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 emit = defineEmits(['close', 'save']);
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object, type: Object,
default: () => {}, default: () => {
},
mode: {
type: String as PropType<'add' | 'edit'>,
default: 'add',
}, },
},
treeData: {
type: Array,
default: () => []
},
areaTree: {
type: Array,
default: () => []
},
mode: {
type: String as PropType<'add' | 'edit'>,
default: 'add',
},
}); });
const areaList = ref<Record<string, any>[]>([]); const areaList = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
@ -133,15 +123,16 @@ const loading = ref<boolean>(false);
const formRef = ref(); const formRef = ref();
const init = { const init = {
parentId: undefined, parentId: undefined,
id: undefined, id: undefined,
name: undefined, name: undefined,
code: undefined, code: undefined,
features: undefined, features: undefined,
children: [], children: [],
properties: { properties: {
type: 'builtin', type: 'builtin',
}, },
sortIndex: props.data.sortIndex || 1
}; };
const modelRef = reactive(init); const modelRef = reactive(init);
@ -149,86 +140,121 @@ const modelRef = reactive(init);
watch( watch(
() => props.data, () => props.data,
() => { () => {
Object.assign(modelRef, {}); Object.assign(modelRef, {});
if (props.mode === 'add' && props.data?.id) { if (props.mode === 'add') {
// //
Object.assign(modelRef, { Object.assign(modelRef, {
...init, ...init,
parentId: props.data.id, ...props.data,
}); });
} else if (props.mode === 'edit') { } else if (props.mode === 'edit') {
// //
Object.assign(modelRef, props.data); Object.assign(modelRef, props.data);
} else { } else {
Object.assign(modelRef, init); Object.assign(modelRef, init);
} }
}, },
{ immediate: true, deep: true }, {immediate: true, deep: true},
); );
const handleCancel = () => { const handleCancel = (data: any) => {
emit('close'); 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 = () => { const handleSave = () => {
formRef.value formRef.value
.validate() .validate()
.then(async (_data: any) => { .then(async (_data: any) => {
loading.value = true; const newData: any = {
const resp = await updateRegion({ ...omit(props.data, ['parentFullName', 'parentId']),
...props.data, ...modelRef,
...modelRef, }
}).finally(() => { newData.fullName = props.data.parentFullName ? props.data.parentFullName + modelRef.name : modelRef.name
loading.value = false; newData.parentId = newData.parentId || ''
});
if (resp.status === 200) { const arr = areaList.value.map(item=>item.code)
onlyMessage('操作成功!');
emit('save'); if (newData.children?.length) {
newData.children = newData.children.map(item => {
if (!item.fullName) {
item.fullName = newData.fullName + item.name
} }
}) item = {
.catch((err: any) => { ...item,
console.log('error', err); 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) => { const vailName = async (_: Record<string, any>, value: string) => {
if (!props?.data?.id && value) { if (!props?.data?.id && value) {
const resp = await validateName(value, props.data.id); const resp = await validateName(value, props.data.id);
if (resp.status === 200 && !resp.result?.passed) { if (resp.status === 200 && !resp.result?.passed) {
return Promise.reject(resp.result?.reason || '区域名称重复'); return Promise.reject('区域名称重复');
} else {
return Promise.resolve();
}
} else { } else {
return Promise.resolve(); return Promise.resolve();
} }
} else {
return Promise.resolve();
}
}; };
const vailCode = async (_: Record<string, any>, value: string) => { const vailCode = async (_: Record<string, any>, value: string) => {
if (!props?.data?.id && value) { if (!props?.data?.id && value) {
const resp = await validateCode(value, props.data.id); const resp = await validateCode(value, props.data.id);
if (resp.status === 200 && !resp.result?.passed) { if (resp.status === 200 && !resp.result?.passed) {
return Promise.reject(resp.result?.reason || '行政区域代码重复'); return Promise.reject('行政区域代码重复');
} else {
return Promise.resolve();
}
} else { } else {
return Promise.resolve(); return Promise.resolve();
} }
} else {
return Promise.resolve();
}
}; };
const onChange = () => { const onChange = () => {
modelRef.features = undefined; modelRef.features = undefined;
}; };
const handleSearch = async () => { watch(() => JSON.stringify(props.treeData), () => {
const resp = await getRegionTree(); const item = JSON.parse(JSON.stringify(props.treeData))
if (resp.success) { areaList.value = item
areaList.value = resp?.result || []; if(props.mode === 'add'){
} modelRef.children = props.areaTree?.[0]?.children
}; }
}, {immediate: true})
onMounted(() => { </script>
handleSearch();
});
</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> <template>
<page-container> <page-container>
<full-page> <full-page fixed>
<div class="region"> <div class="region">
<div class="left"> <div class="left">
<LeftTree /> <LeftTree @select="onSelect" />
<div class="mask" v-if="type === 'edit'"></div>
</div> </div>
<div class="right"> <div class="right">
<Map :path="path" :type="mapType" /> <Map ref="mapRef" :selectCode="selectCode" />
</div> </div>
</div> </div>
</full-page> </full-page>
@ -17,18 +16,13 @@
<script setup lang="ts" name="RegionMange"> <script setup lang="ts" name="RegionMange">
import LeftTree from './LeftTree/index.vue' import LeftTree from './LeftTree/index.vue'
import Map from './MapTool/map.vue' import Map from './MapTool/map.vue'
import FullPage from "components/Layout/FullPage.vue"; import FullPage from "@/components/Layout/FullPage.vue";
import { provide } from 'vue'
const path = ref([[121.5273285, 31.21515044], [121.5293285, 31.21515044], [121.5293285, 31.21915044], [121.5273285, 31.21515044]]) const selectCode = ref('')
const type = ref<'view' | 'edit'>('view')
const mapType = ref<string>('create')
provide('system-region', { const onSelect = (dt: string) => {
type, selectCode.value = dt
mapType: '', }
path
})
</script> </script>
<style lang="less" scoped> <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 { USER_CENTER_MENU_DATA } from '@/views/init-home/data/baseMenu'
import { import {
getRoleDetails_api, getRoleDetails_api,
updateRole_api, updateRole_api,
editRole_api, editRole_api,
updatePrimissTree_api, updatePrimissTree_api, clearPrimissTree_api,
} from '@/api/system/role'; } from '@/api/system/role';
import { onlyMessage } from '@/utils/comm'; import { onlyMessage } from '@/utils/comm';
@ -85,13 +85,21 @@ const form = reactive({
clickSave: () => { clickSave: () => {
// formRef.value?.validate().then(() => { // formRef.value?.validate().then(() => {
// const updateRole = editRole_api(roleId, form.data); // const updateRole = editRole_api(roleId, form.data);
if(form.menus?.length > 0){
const updateTree = updatePrimissTree_api(roleId, { const updateTree = updatePrimissTree_api(roleId, {
menus: form.menus, menus: form.menus,
}); });
Promise.all([ updateTree]).then((resp) => { Promise.all([ updateTree]).then((resp) => {
onlyMessage('操作成功'); onlyMessage('操作成功');
// jumpPage(`system/Role`); // jumpPage(`system/Role`);
}) })
} else {
clearPrimissTree_api(roleId).then(resp => {
if(resp.success){
onlyMessage('操作成功');
}
})
}
// }); // });
}, },
}); });

View File

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

View File

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

View File

@ -98,7 +98,7 @@ export default defineConfig(({ mode}) => {
// target: 'http://120.77.179.54:8844', // 120测试 // target: 'http://120.77.179.54:8844', // 120测试
target: 'http://192.168.33.46:8844', // 本地开发环境 target: 'http://192.168.33.46:8844', // 本地开发环境
// target: 'http://192.168.32.167:8844', // 本地开发环境1 // 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.207:8844', // 刘本地
// target: 'http://192.168.32.187:8844', // 谭本地 // target: 'http://192.168.32.187:8844', // 谭本地
// target: 'http://192.168.33.66:8844', // 苟本地 // target: 'http://192.168.33.66:8844', // 苟本地