Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
jackhoo_98 2023-03-30 13:47:16 +08:00
commit fc3d0d2d12
28 changed files with 251 additions and 152 deletions

View File

@ -13,7 +13,7 @@ export const delApply_api = (id: string) => server.remove(`/application/${id}`)
// 获取组织列表
export const getDepartmentList_api = (params: any) => server.get(`/organization/_all/tree`, params);
// 获取应用详情
export const getAppInfo_api = (id: string) => server.get(`/application/${id}`);
export const getAppInfo_api = (id: string) => server.get<any>(`/application/${id}`);
// 新增应用
export const addApp_api = (data: object) => server.post(`/application`, data);
// 更新应用

View File

@ -176,8 +176,8 @@ const objectValue = ref<string>('');
const handleItemModalSubmit = () => {
myValue.value = objectValue.value.replace(/[\r\n]\s*/g, '');
modalVis.value = false;
emit('update:modelValue', objectValue.value);
emit('change', objectValue.value)
emit('update:modelValue', myValue.value);
};
//
@ -192,23 +192,23 @@ const handleFileChange = (info: UploadChangeParam<UploadFile<any>>) => {
};
const selectChange = (e: string, option: any) => {
emit('change', e, option)
emit('update:modelValue', myValue.value);
emit('change', e, option)
}
const timeChange = (e: any) => {
emit('change', e)
emit('update:modelValue', myValue.value);
emit('change', e)
}
const inputChange = (e: any) => {
emit('change', e && e.target ? e.target.value : e)
emit('update:modelValue', myValue.value);
emit('change', e && e.target ? e.target.value : e)
}
const dateChange = (e: any) => {
emit('change', e)
emit('update:modelValue', myValue.value);
emit('change', e)
}
myValue.value = props.modelValue

View File

@ -122,7 +122,7 @@ const showNotification = (message: string, description: string, key?: string, sh
if (show) {
Notification.error({
key,
message,
message: '',
description
})
}

View File

@ -265,7 +265,7 @@ const unBind = (id: string) => {
};
const clickBind = (id: string) => {
window.open(
`${location.host}${BASE_API_PATH}/application/sso/${id}/login?autoCreateUser=false`,
`${BASE_API_PATH}/application/sso/${id}/login?autoCreateUser=false`,
);
localStorage.setItem('onBind', 'false');
localStorage.setItem('onLogin', 'yes');

View File

@ -6,7 +6,7 @@
<span>精简模式下参数只支持输入框的方式录入</span>
</j-space>
</div>
<j-tabs v-model="activeKey" tab-position="left">
<j-tabs v-model="activeKey" tab-position="left" @change="onTabChange" :destroyInactiveTabPane="true">
<j-tab-pane v-for="func in newFunctions" :key="func.id">
<template #tab>
<Ellipsis style="width: 100px; text-align: left">
@ -95,7 +95,7 @@
:ref="`result${func.id}Ref`"
class="execute-result"
>
{{ func.executeResult }}
{{ executeResult || '' }}
</span>
</j-col>
</j-row>
@ -135,6 +135,8 @@ const columns = ref([
},
]);
const executeResult = ref('')
//
const newFunctions = computed(() => {
const result: any = [];
@ -205,7 +207,7 @@ const handleExecute = async (func: any) => {
);
if (!success) return;
message.success('操作成功');
func.executeResult = result instanceof Array ? result[0] : result;
executeResult.value = result instanceof Array ? result[0] : result;
proxy?.$forceUpdate();
})
.catch((err: any) => {
@ -216,8 +218,13 @@ const handleExecute = async (func: any) => {
* 清空
*/
const handleClear = (func: any) => {
executeResult.value = ''
proxy?.$refs[`${func.id}Ref`][0].resetFields();
};
const onTabChange = (_key: string) => {
executeResult.value = ''
}
</script>
<style lang="less" scoped>

View File

@ -220,7 +220,7 @@
</template>
<template #createTime="slotProps">
<span>{{
dayjs(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss')
slotProps?.createTime ? dayjs(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss') : ''
}}</span>
</template>
<template #action="slotProps">

View File

@ -47,7 +47,7 @@
</template>
<template #registerTime="slotProps">
<span>{{
dayjs(slotProps.registerTime).format('YYYY-MM-DD HH:mm:ss')
slotProps?.registerTime ? dayjs(slotProps.registerTime).format('YYYY-MM-DD HH:mm:ss') : ''
}}</span>
</template>
</JProTable>

View File

@ -0,0 +1,49 @@
<template>
<page-container>
<iframe
:src="iframeUrl"
frameBorder="0"
style="width: 100%; height: calc(100vh - 140px)"
></iframe>
</page-container>
</template>
<script setup lang="ts">
import { TOKEN_KEY } from '@/utils/variable';
import { LocalStore } from '@/utils/comm';
import { getAppInfo_api } from '@/api/system/apply';
const iframeUrl = ref<string>('');
const handle = async (appId: string, url: string) => {
const res = await getAppInfo_api(appId);
let menuUrl: any = url;
if (res.status === 200) {
// console.log(res.result);
if (res.result.page.routeType === 'hash') {
menuUrl = `${url}`;
}
if (res.result.provider === 'internal-standalone') {
//{baseUrl}/api/application/sso/{appId}/login?redirect={menuUrl}
const urlStandalone = `${res.result.page.baseUrl}/api/application/sso/${appId}/login?redirect=${menuUrl}?layout=false`;
iframeUrl.value = urlStandalone;
// console.log(urlStandalone);
} else if (res.result.provider === 'internal-integrated') {
const tokenUrl = `${
res.result.page.baseUrl
}?X-Access-Token=${LocalStore.get(TOKEN_KEY)}`;
iframeUrl.value = tokenUrl;
} else {
const urlOther = `${res.result.page.baseUrl}/${menuUrl}`;
iframeUrl.value = urlOther;
}
}
};
watchEffect(() => {
const params = location.pathname.split('/')?.[1];
const url = location.pathname.split('/').slice(2).join('/');
// console.log(params, url);
handle(params, url);
});
</script>

View File

@ -1366,16 +1366,7 @@ export default [
},
],
},
{
id: 'action',
name: '启/禁用',
permissions: [
{
permission: 'firmware-upgrade-task-manager',
actions: ['save'],
}
],
},
],
accessSupport: { text: "间接支持", value: "indirect" },
supportDataAccess: false,
@ -1688,7 +1679,7 @@ export default [
],
accessSupport: { text: "支持", value: "support" },
supportDataAccess: true,
assetType: 'ataCollectCollector'
assetType: 'dataCollectCollector'
},
],
},

View File

@ -114,7 +114,6 @@ const functionData = computed(() => {
const rules = [{
validator(_: string, value: any) {
console.log(value)
debugger
if (!value?.length && functionData.value.length) {
return Promise.reject('请输入功能值')
} else {
@ -137,6 +136,7 @@ const onSelect = (v: string, item: any) => {
}
const callDataChange = (v: any[]) => {
console.log('callDataChange',v)
emit('update:functionParameters', v)
}

View File

@ -65,10 +65,12 @@ const handleOptions = computed(() => {
{
label: _item.trueText || true,
value: _item.trueValue || true,
id: _item.trueValue || true,
},
{
label: _item.falseText || false,
value: _item.falseValue || false,
id: _item.falseValue || false,
},
];
}
@ -77,6 +79,7 @@ const handleOptions = computed(() => {
return {
label: i.text,
value: i.value,
id: i.value,
};
});
}

View File

@ -136,10 +136,12 @@ const handleOptions = computed(() => {
{
label: _item.trueText || true,
value: _item.trueValue || true,
id: _item.trueValue || true,
},
{
label: _item.falseText || false,
value: _item.falseValue || false,
id: _item.falseValue || false,
},
];
}
@ -148,6 +150,7 @@ const handleOptions = computed(() => {
return {
label: i.text,
value: i.value,
id: i.value,
};
});
}

View File

@ -35,7 +35,10 @@ const rules = [{
validator(_: any, v?: ActionsType) {
console.log('validator',v)
if (v?.executor === 'device') {
if(!v.device?.productId || !v.device?.selectorValues) {
if(
!v.device?.productId || //
!v.device?.selectorValues //
) {
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
}
}
@ -53,12 +56,13 @@ const formTouchOff = () => {
const checkDeviceDelete = async () => {
const item = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device
const proResp = await queryProductList({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: item!.productId }]}]})
if (proResp.success && (proResp.result as any)?.total === 0) { //
if (proResp.success && (proResp.result as any)?.total === 0 && item && item.productId) { //
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.productId = undefined
formTouchOff()
return
}
if (item?.selector === 'fixed') {
console.log(item)
const deviceList = item!.selectorValues?.map(item => item.value) || []
const deviceResp = await deviceQuery({ terms: [{ terms: [{ column: 'id', termType: 'in', value: deviceList.toString() }]}]})
if (deviceResp.success && (deviceResp.result as any)?.total < (item!.selectorValues?.length || 0)) { //

View File

@ -1,9 +1,11 @@
<template>
<j-menu class='scene-dropdown-menus' @click='click' :selectedKeys='[myValue]'>
<j-menu-item v-for='item in myOptions' :key='item.value' :title='item.label'>
<Ellipsis >
{{ item.label }}
</Ellipsis>
<div>
<Ellipsis >
{{ item.label }}
</Ellipsis>
</div>
</j-menu-item>
</j-menu>
</template>
@ -60,7 +62,7 @@ const handleBoolean = (key: string) => {
const click = (e: any) => {
const _key = ['true', 'false'].includes(e.key) ? handleBoolean(e.key) : e.key
const option = getOption(myOptions.value, _key, props.valueName)
myValue.value = _key
myValue.value = e.key
emit('update:value', _key)
emit('click', _key, {
key: _key,
@ -69,7 +71,7 @@ const click = (e: any) => {
}
watch(() => props.value, () => {
myValue.value = props.value
myValue.value = isBoolean(props.value) ? String(props.value) : props.value
}, { immediate: true})
</script>

View File

@ -8,7 +8,9 @@
>
<template #bodyCell="{ column, record, index }">
<template v-if='column.dataIndex === "name"'>
{{ record.name }}
<Ellipsis>
{{ record.name }}
</Ellipsis>
</template>
<template v-if='column.dataIndex === "type"'>
{{ record.type }}
@ -29,12 +31,14 @@
</j-tooltip>
</template>
<template v-if='column.dataIndex === "value"'>
<ValueItem
v-model:modelValue='record.value'
:itemType="record.type"
:options="handleOptions(record)"
@change='valueChange'
/>
<div style='max-width: 260px'>
<ValueItem
v-model:modelValue='record.value'
:itemType="record.type"
:options="handleOptions(record)"
@change='valueChange'
/>
</div>
</template>
</template>
</j-table>
@ -42,7 +46,6 @@
<script setup lang='ts' name='FunctionCall'>
import type { PropType } from 'vue'
import { debounce } from 'lodash-es'
type Emit = {
(e: 'change', data: Array<{ name: string, value: any}>): void
@ -69,7 +72,8 @@ const dataSource = reactive<{value: any[]}>({
const columns = [
{
title: '参数名称',
dataIndex: 'name'
dataIndex: 'name',
width: 300
},
{
title: '类型',
@ -95,11 +99,13 @@ const handleOptions = (record: any) => {
}
const valueChange = () => {
const _value = dataSource.value.map(item => ({
name: item.id, value: item.value
}))
emit('change', _value)
const _value = dataSource.value.map(item => {
return {
name: item.id, value: item.value
}
})
emit('update:value', _value)
emit('change', _value)
}
watch(() => props.data, () => {

View File

@ -162,6 +162,7 @@ watchEffect(() => {
const option = getOption(_options, props.value as string, props.valueName) // label
myValue.value = props.value
mySource.value = props.source
console.log(option, _options, props.valueName)
if (option) {
label.value = option[props.labelName] || option.name
treeOpenKeys.value = openKeysByTree(_options, props.value, props.valueName)

View File

@ -177,8 +177,8 @@ const handOptionByColumn = (option: any) => {
if (option.dataType === 'boolean') {
valueOptions.value = option.options?.map((item: any) => ({ ...item, label: item.name, value: item.id})) || [
{ label: '是', value: true },
{ label: '否', value: false },
{ label: '是', value: true, id: true },
{ label: '否', value: false, id: false },
]
} else if(option.dataType === 'enum') {
valueOptions.value = option.options?.map((item: any) => ({ ...item, label: item.name, value: item.id})) || []

View File

@ -215,9 +215,20 @@ defineExpose({
resolve(data)
})
})
Object.assign(formModel, props.value)
formModel.when = props.value.when || []
watchEffect(() => {
if(props.value?.period.unit === 'hours') {
unitMax.
value = 99999
} else {
unitMax.value = 99
}
})
</script>
<style scoped lang='less'>

View File

@ -73,7 +73,7 @@ export const handleTimerOptions = (timer: OperationTimer):TimerOption => {
const whenStrArr = continuousValue(timer.when! || [], timer!.trigger);
const whenStrArr3 = whenStrArr.splice(0, 3);
when += whenStrArr3.join('、');
when += `${timer.when!.length}`;
when += `...${timer.when!.length}`;
}
if (timer.once) {

View File

@ -1718,20 +1718,20 @@ function getInfo(id: string) {
getAppInfo_api(id).then((resp: any) => {
// headersparameters, keylabel
if (resp.result.apiClient) {
resp.result.apiClient.headers = resp.result.apiClient.headers.map(
resp.result.apiClient.headers = resp.result.apiClient.headers?.map(
(m: any) => ({
...m,
label: m.key,
}),
);
resp.result.apiClient.parameters =
resp.result.apiClient.parameters.map((m: any) => ({
resp.result.apiClient.parameters?.map((m: any) => ({
...m,
label: m.key,
}));
}
if (resp.result.page) {
resp.result.page.parameters = resp.result.page.parameters.map(
resp.result.page.parameters = resp.result.page.parameters?.map(
(m: any) => ({
...m,
label: m.key,
@ -1741,7 +1741,7 @@ function getInfo(id: string) {
form.data = {
...initForm, // , . : bug#10892
...resp.result,
integrationModes: resp.result.integrationModes.map(
integrationModes: resp.result.integrationModes?.map(
(item: any) => item.value,
),
} as formType;
@ -1753,7 +1753,7 @@ function getRoleIdList() {
getRoleList_api().then((resp) => {
if (resp.status === 200) {
const result = resp.result as dictType;
form.roleIdList = result.map((item) => ({
form.roleIdList = result?.map((item) => ({
label: item.name,
value: item.id,
}));
@ -1821,13 +1821,13 @@ function clickSave() {
// headersparamslabelkey
if (params.integrationModes.includes('apiClient')) {
params.apiClient.headers = params.apiClient.headers.map(
params.apiClient.headers = params.apiClient.headers?.map(
(m: any) => ({
...m,
key: m.label,
}),
);
params.apiClient.parameters = params.apiClient.parameters.map(
params.apiClient.parameters = params.apiClient.parameters?.map(
(m: any) => ({
...m,
key: m.label,
@ -1835,7 +1835,7 @@ function clickSave() {
);
}
if (params.integrationModes.includes('page')) {
params.page.parameters = params.page.parameters.map((m: any) => ({
params.page.parameters = params.page.parameters?.map((m: any) => ({
...m,
key: m.label,
}));

View File

@ -23,58 +23,59 @@
</div>
<div class="tree">
<jTree
v-if="treeData.length > 0"
:tree-data="treeData"
v-model:selected-keys="selectedKeys"
v-model:expandedKeys="expandedKeys"
:fieldNames="{ key: 'id' }"
>
<template #title="{ name, data }">
<span>{{ name }}</span>
<span class="func-btns" @click="(e) => e.stopPropagation()">
<PermissionButton
:hasPermission="`${permission}:update`"
type="link"
:tooltip="{
title: '编辑',
}"
@click="openDialog(data)"
>
<AIcon type="EditOutlined" />
</PermissionButton>
<PermissionButton
:hasPermission="`${permission}:add`"
type="link"
:tooltip="{
title: '新增子组织',
}"
@click="
openDialog({
...data,
id: '',
parentId: data.id,
})
"
>
<AIcon type="PlusCircleOutlined" />
</PermissionButton>
<PermissionButton
type="link"
:hasPermission="`${permission}:delete`"
:tooltip="{ title: '删除' }"
:popConfirm="{
title: `确定要删除吗`,
onConfirm: () => delDepartment(data.id),
}"
>
<AIcon type="DeleteOutlined" />
</PermissionButton>
</span>
</template>
</jTree>
<div class="loading" v-else-if="loading"><j-spin /></div>
<j-empty v-else description="暂无数据" />
<j-spin :spinning='loading'>
<jTree
v-if="treeData.length > 0"
:tree-data="treeData"
v-model:selected-keys="selectedKeys"
v-model:expandedKeys="expandedKeys"
:fieldNames="{ key: 'id' }"
>
<template #title="{ name, data }">
<span>{{ name }}</span>
<span class="func-btns" @click="(e) => e.stopPropagation()">
<PermissionButton
:hasPermission="`${permission}:update`"
type="link"
:tooltip="{
title: '编辑',
}"
@click="openDialog(data)"
>
<AIcon type="EditOutlined" />
</PermissionButton>
<PermissionButton
:hasPermission="`${permission}:add`"
type="link"
:tooltip="{
title: '新增子组织',
}"
@click="
openDialog({
...data,
id: '',
parentId: data.id,
})
"
>
<AIcon type="PlusCircleOutlined" />
</PermissionButton>
<PermissionButton
type="link"
:hasPermission="`${permission}:delete`"
:tooltip="{ title: '删除' }"
:popConfirm="{
title: `确定要删除吗`,
onConfirm: () => delDepartment(data.id),
}"
>
<AIcon type="DeleteOutlined" />
</PermissionButton>
</span>
</template>
</jTree>
<j-empty v-else description="暂无数据" />
</j-spin>
</div>
<!-- 编辑弹窗 -->
<EditDepartmentDialog
@ -227,6 +228,9 @@ function init() {
.left-tree-container {
padding-right: 24px;
border-right: 1px solid #f0f0f0;
display: flex;
height: 100%;
flex-direction: column;
.add-btn {
margin: 24px 0;
@ -262,10 +266,17 @@ function init() {
}
}
.loading {
.tree {
overflow-y: auto;
overflow-x: hidden;
.loading {
display: flex;
width: 100%;
justify-content: center;
margin-top: 20px;
}
}
}
</style>

View File

@ -1,28 +1,30 @@
<template>
<page-container>
<div class="department-container">
<div class="department-warp">
<div class='department-container'>
<div class="left">
<LeftTree @change="(id) => (departmentId = id)" />
<LeftTree @change="(id) => (departmentId = id)" />
</div>
<div class="right">
<j-tabs v-model:activeKey="activeKey" destroyInactiveTabPane>
<j-tab-pane key="product" tab="产品">
<Product
:parentId="departmentId"
@open-device-bind="openDeviceBind"
/>
</j-tab-pane>
<j-tab-pane key="device" tab="设备">
<Device
:parentId="departmentId"
v-model:bindBool="bindBool"
/>
</j-tab-pane>
<j-tab-pane key="user" tab="用户">
<User :parentId="departmentId" />
</j-tab-pane>
</j-tabs>
<j-tabs v-model:activeKey="activeKey" destroyInactiveTabPane>
<j-tab-pane key="product" tab="产品">
<Product
:parentId="departmentId"
@open-device-bind="openDeviceBind"
/>
</j-tab-pane>
<j-tab-pane key="device" tab="设备">
<Device
:parentId="departmentId"
v-model:bindBool="bindBool"
/>
</j-tab-pane>
<j-tab-pane key="user" tab="用户">
<User :parentId="departmentId" />
</j-tab-pane>
</j-tabs>
</div>
</div>
</div>
</page-container>
</template>
@ -45,19 +47,25 @@ const openDeviceBind = () => {
</script>
<style lang="less" scoped>
.department-container {
display: flex;
background-color: #fff;
padding: 24px;
.department-warp {
background-color: #fff;
padding: 24px;
.department-container {
position: relative;
.left {
flex-basis: 300px;
}
.right {
width: calc(100% - 300px);
:deep(.ant-tabs-nav-wrap) {
padding-left: 24px;
}
}
.left {
position: absolute;
height: 100%;
width: 300px;
}
.right {
width: calc(100% - 316px);
margin-left: 316px;
:deep(.ant-tabs-nav-wrap) {
padding-left: 24px;
}
}
}
}
</style>

View File

@ -553,7 +553,7 @@ watch(
);
watch(
() => dialogs.editShow,
() => dialogs.addShow,
(val: boolean) => {
if (!val) tableData.selectedRows = [];
},

View File

@ -155,6 +155,7 @@ const handleParams = (params: any) => {
//
const tableRef = ref<Record<string, any>>({}); //
const table = reactive({
_selectedRowKeys: [] as string[],

View File

@ -189,12 +189,13 @@
style="width: 500px"
placeholder="请选择资产类型"
show-search
:options="form.assetsType"
>
<j-select-option
<!-- <j-select-option
v-for="item in form.assetsType"
:value="item.value"
>{{ item.label }}</j-select-option
>
> -->
</j-select>
</j-form-item>

View File

@ -6,6 +6,7 @@
:pagination="false"
:rowKey="'id'"
:scroll="{ y: '500px' }"
ref="treeRef"
>
<!-- 表头 -->
<template #headerCell="{ column }">
@ -99,13 +100,14 @@
<script setup lang="ts">
import { cloneDeep } from 'lodash-es';
import { getPrimissTree_api } from '@/api/system/role';
import { getCurrentInstance } from 'vue';
const emits = defineEmits(['update:selectItems']);
const route = useRoute();
const props = defineProps({
selectItems: Array,
});
const treeRef = ref();
let { ctx: that, proxy } = getCurrentInstance();
const columns = [
{
title: '菜单权限',
@ -282,7 +284,6 @@ function menuChange(
row: tableItemType,
setButtonBool: boolean = true,
): undefined {
console.log(row, 'test');
//
if (setButtonBool) {
if (row.buttons && row.buttons.length > 0)
@ -334,6 +335,7 @@ function menuChange(
indeterminate.value = false;
}
emits('update:selectItems', selectList); //
treeRef.value.$forceUpdate();
}
/**
@ -430,7 +432,6 @@ function setStatus(
prop: 'children' | 'buttons' = 'children',
) {
const childrens = target[prop] as any[];
if (childrens && childrens instanceof Array) {
//
const indeterminateLen = childrens.filter(

View File

@ -92,8 +92,8 @@ export default defineConfig(({ mode}) => {
[env.VITE_APP_BASE_API]: {
// target: 'http://192.168.33.22:8800',
// target: 'http://192.168.32.244:8881',
target: 'http://120.77.179.54:8844', // 120测试
// target: 'http://192.168.33.46:8844', // 本地开发环境
// target: 'http://120.77.179.54:8844', // 120测试
target: 'http://192.168.33.46:8844', // 本地开发环境
ws: 'ws://192.168.33.46:8844',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')

View File

@ -3700,8 +3700,8 @@ jetlinks-store@^0.0.3:
jetlinks-ui-components@^1.0.5:
version "1.0.5"
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#a8c912f424b8e6c3e0aa8e2aa9ddcc59fe13cd3a"
integrity sha512-zZsVbqG7sLfKsizK+8sT0bCmAz7rEu/qoS5yYSEUzGMvTGQU3Q5W6qdT/5o5v92BYFP+1Kud1l5CNhA3e3NtWQ==
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#d5d2b919f89cede8e7236149afdb9ab1e5684e9b"
integrity sha512-vWNz2YT1bsfh0xAAnJLEfFcukeX4QE3HyloaJJp3xU9wVbwtS38sdKEQepHZdsDKwzgKWdL03/o/qE3hgBKpwQ==
dependencies:
"@vueuse/core" "^9.12.0"
ant-design-vue "^3.2.15"