This commit is contained in:
xuziqiang
2024-05-15 17:29:42 +08:00
commit d0155dbe3c
7296 changed files with 1832517 additions and 0 deletions

View File

@@ -0,0 +1,285 @@
<template>
<div class="ns-view">
<a-spin class="ns-view-spinning" :spinning="isLoading" :key="'addForm_' + $route.name">
<ns-page-header class="ns-page-header" :sticky="sticky">
<template #title>
<div class="title" @click="navigateBack" v-if="showBack">
<!-- <left-outlined style="color: rgba(0, 0, 0, 0.4)" /> {{ title }} -->
<ns-icon name="left" /> <div class="text">{{ title }}</div>
</div>
<div class="title" v-else>
{{ title }}
</div>
</template>
<template #extra>
<ns-button v-if="okText" type="primary" @click="submit" :disabled="!validateResult">{{
okText
}}</ns-button>
<template v-for="item in getActions" :key="item.name">
<template v-if="item.children">
<template v-if="getDropdownActions(item.children).length > 0">
<a-dropdown :trigger="['hover']" :dropMenuList="item.children">
<a class="ant-dropdown-link" @click.prevent>
{{ item.label }}
<CaretDownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item
style="padding: 2px 5px"
v-for="itemChild in getDropdownActions(item.children)"
:key="itemChild.name"
@click="itemChild.finalHandle(data, itemChild.name)">
<a-button type="link" :disabled="itemChild.dynamicDisabled">
{{ itemChild.label }}
</a-button>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</template>
<template v-else>
<ns-button
:type="item.type || 'default'"
:disabled="item.dynamicDisabled"
@click="item.finalHandle(data, item.name)">
<ns-v-node :content="item.label" />
</ns-button>
</template>
</template>
<ns-button v-if="cancelText" @click="navigateBack">{{ cancelText }}</ns-button>
</template>
</ns-page-header>
<div class="ns-add-form">
<ns-form ref="nsFormElRef" v-bind="getBindValue">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</ns-form>
<slot name="nsCustomFormItem"></slot>
</div>
</a-spin>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, ref } from 'vue';
import { http } from '/nerv-lib/util/http';
import { NsMessage } from '/nerv-lib/component/message';
import { useNavigate } from '/nerv-lib/use/use-navigate';
import { cloneDeep, isEmpty } from 'lodash-es';
import { Action, useAction } from '/nerv-lib/use';
import { useRoute } from 'vue-router';
import { CaretDownOutlined, LeftOutlined } from '@ant-design/icons-vue';
import { useApi, HttpRequestConfig } from '/nerv-lib/use/use-api';
interface ColumnActions {
back: Boolean;
actions: Action[];
}
export default defineComponent({
name: 'NsViewAddFormV2',
components: {
CaretDownOutlined,
LeftOutlined,
},
props: {
api: {
type: String,
required: true,
},
showBack: {
type: Boolean,
default: true,
},
sticky: {
type: Boolean,
default: true,
},
title: String,
params: {
type: Object,
default: () => ({}),
},
data: {
type: Object,
default: () => ({}),
},
dataHandle: Function,
okText: {
type: String,
default: '确 定',
},
cancelText: {
type: String,
default: '取 消',
},
headActions: {
type: Object as PropType<ColumnActions>,
default: () => ({
actions: [],
}),
},
customDataApi: {
type: Function,
},
},
setup(props, { attrs }) {
const nsFormElRef = ref(null);
const isLoading = ref(false);
const { navigateBackV2: navigateBack } = useNavigate();
const route = useRoute();
const { httpRequest } = useApi();
const getData = computed(() => {
return {
...route.query,
...route.params,
formRef: nsFormElRef.value,
};
});
const getBindValue = computed(() => ({
...attrs,
...props,
title: '',
}));
const validateResult = computed(() => {
return nsFormElRef.value?.validateResult;
});
//提交
function submit() {
nsFormElRef.value
.triggerSubmit()
.then((data: any) => {
isLoading.value = true;
data = props.dataHandle ? props.dataHandle(data) : data;
const { api } = props;
const requestConfig: HttpRequestConfig = { method: 'POST' };
const { params } = route;
httpRequest({ api, params: data, pathParams: params, requestConfig })
.then(() => {
NsMessage.success('操作成功', 1, () => {
isLoading.value = false;
navigateBack();
});
})
.catch(() => {
isLoading.value = false;
});
})
.catch(() => {});
}
// function request() {
// const { query } = route;
// if (!props.detailApi) return;
// if (props.customDataApi) {
// props.customDataApi(props.detailApi, query).then((res) => {
// getBindValue.value.data = res.data;
// isLoading.value = false;
// });
// } else {
// http.get(props.detailApi, query).then((res) => {
// getBindValue.value.data = res.data;
// isLoading.value = false;
// });
// }
// }
// request();
const { transformAction } = useAction();
const getActions = computed(() => {
let actions = cloneDeep(props.headActions.actions);
actions = actions.map((action) => {
return transformAction(action, getData.value);
});
return actions;
});
const getDropdownActions = (actions) => {
actions = actions.map((action) => {
return transformAction(action, getData.value);
});
return actions;
};
return {
nsFormElRef,
validateResult,
submit,
getBindValue,
isLoading,
navigateBack,
getActions,
getDropdownActions,
};
},
});
</script>
<style lang="less" scoped>
.ns-view {
min-height: 100%;
height: 100%;
background: #e5ebf0;
}
:deep(.ant-spin-nested-loading) {
min-height: 100%;
height: 100%;
}
:deep(.ant-spin-container) {
min-height: 100%;
height: 100%;
}
:deep(.ant-divider) {
display: none;
}
.ns-page-header {
margin-bottom: 0 !important;
padding: 7px 16px !important;
width: calc(100% + 32px);
margin-left: -16px;
.title {
cursor: pointer;
font-size: 18px !important;
display: flex;
align-items: center;
.text {
margin-left: 6px;
}
}
}
.ns-add-form {
border-top: 16px solid #e5ebf0;
padding: 16px 21px;
background: #fff;
height: calc(100% - 47px);
:deep(.ns-form) {
.ns-form-item .ns-form-body .ns-child-form-title {
padding-top: 0;
}
&:after {
display: none;
}
}
:deep(.ns-child-form-title) {
&:after {
content: '';
width: 75px;
height: 7px;
display: block;
background: linear-gradient(90deg, @primary-color 0%, #fff 82.67%);
margin-left: 2px;
margin-top: -7px;
}
}
}
:deep(.ns-form.ns-vertical-form) {
padding-top: 16px !important;
}
</style>

View File

@@ -0,0 +1,282 @@
<template>
<div class="ns-view">
<a-spin class="ns-view-spinning" :spinning="isLoading" :key="'addForm_' + $route.name">
<ns-page-header class="ns-page-header" :sticky="sticky">
<template #title>
<div class="title" @click="onBack" v-if="showBack">
<!-- <left-outlined style="color: rgba(0, 0, 0, 0.4)" /> {{ title }} -->
<ns-icon name="left" /> <div class="text">{{ title }}</div>
</div>
<div class="title" v-else>
{{ title }}
</div>
</template>
<template #extra>
<ns-button v-if="okText" type="primary" @click="submit" :disabled="!validateResult">{{
okText
}}</ns-button>
<template v-for="item in getActions" :key="item.name">
<template v-if="item.children">
<template v-if="getDropdownActions(item.children).length > 0">
<a-dropdown :trigger="['hover']" :dropMenuList="item.children">
<a class="ant-dropdown-link" @click.prevent>
{{ item.label }}
<CaretDownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item
style="padding: 2px 5px"
v-for="itemChild in getDropdownActions(item.children)"
:key="itemChild.name"
@click="itemChild.finalHandle(data, itemChild.name)">
<a-button type="link" :disabled="itemChild.dynamicDisabled">
{{ itemChild.label }}
</a-button>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</template>
<template v-else>
<ns-button
:type="item.type || 'default'"
:disabled="item.dynamicDisabled"
@click="item.finalHandle(data, item.name)">
<ns-v-node :content="item.label" />
</ns-button>
</template>
</template>
<ns-button v-if="cancelText" @click="navigateBack">{{ cancelText }}</ns-button>
</template>
</ns-page-header>
<div class="ns-add-form">
<ns-form ref="nsFormElRef" v-bind="getBindValue">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</ns-form>
<slot name="nsCustomFormItem"></slot>
</div>
</a-spin>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, ref } from 'vue';
import { http } from '/nerv-lib/util/http';
import { NsMessage } from '/nerv-lib/component/message';
import { useNavigate } from '/nerv-lib/use/use-navigate';
import { cloneDeep } from 'lodash-es';
import { Action, useAction } from '/nerv-lib/use';
import { useRoute } from 'vue-router';
import { CaretDownOutlined, LeftOutlined } from '@ant-design/icons-vue';
import { useApi, HttpRequestConfig } from '/nerv-lib/use/use-api';
interface ColumnActions {
back: Boolean;
actions: Action[];
}
export default defineComponent({
name: 'NsViewAddForm',
components: {
CaretDownOutlined,
LeftOutlined,
},
props: {
api: {
type: String,
required: true,
},
showBack: {
type: Boolean,
default: true,
},
detailApi: {
type: String,
required: true,
},
sticky: {
type: Boolean,
default: true,
},
title: String,
params: {
type: Object,
default: () => ({}),
},
data: {
type: Object,
default: () => ({}),
},
dataHandle: Function,
okText: {
type: String,
default: '确 定',
},
cancelText: {
type: String,
default: '取 消',
},
headActions: {
type: Object as PropType<ColumnActions>,
default: () => ({
actions: [],
}),
},
customBack: {
type: String || Function,
},
},
setup(props, { attrs }) {
const nsFormElRef = ref(null);
const isLoading = ref(false);
const { navigateBackV2: navigateBack } = useNavigate();
const route = useRoute();
const { httpRequest } = useApi();
const getData = computed(() => {
return {
...route.query,
...route.params,
formRef: nsFormElRef.value,
};
});
const getBindValue = computed(() => ({
...attrs,
...props,
title: '',
}));
const validateResult = computed(() => {
return nsFormElRef.value?.validateResult;
});
function submit() {
nsFormElRef.value
.triggerSubmit()
.then((data: any) => {
isLoading.value = true;
data = props.dataHandle ? props.dataHandle(data) : data;
const { api } = props;
const requestConfig: HttpRequestConfig = { method: 'POST' };
const { params } = route;
httpRequest({ api, params: data, pathParams: params, requestConfig })
.then(() => {
NsMessage.success('操作成功', 1, () => {
isLoading.value = false;
navigateBack();
});
})
.catch(() => {
isLoading.value = false;
});
})
.catch(() => {});
}
const { transformAction } = useAction();
const getActions = computed(() => {
let actions = cloneDeep(props.headActions.actions);
actions = actions.map((action) => {
return transformAction(action, getData.value);
});
return actions;
});
const getDropdownActions = (actions) => {
actions = actions.map((action) => {
return transformAction(action, getData.value);
});
return actions;
};
return {
nsFormElRef,
validateResult,
submit,
getBindValue,
isLoading,
navigateBack,
getActions,
getDropdownActions,
};
},
methods: {
onBack() {
//debugger;
if (this.customBack) {
this.customBack();
} else {
this.navigateBack();
}
// this.$router.go(-1);
},
},
});
</script>
<style lang="less" scoped>
.ns-view {
min-height: 100%;
height: 100%;
background: #e5ebf0;
}
:deep(.ant-spin-nested-loading) {
min-height: 100%;
height: 100%;
}
:deep(.ant-spin-container) {
min-height: 100%;
height: 100%;
}
:deep(.ant-divider) {
display: none;
}
.ns-page-header {
margin-bottom: 0 !important;
padding: 7px 16px !important;
width: calc(100% + 32px);
margin-left: -16px;
.title {
cursor: pointer;
font-size: 18px !important;
display: flex;
align-items: center;
.text {
margin-left: 6px;
}
}
}
.ns-add-form {
border-top: 16px solid #e5ebf0;
padding: 16px 21px;
background: #fff;
height: calc(100% - 47px);
:deep(.ns-form) {
.ns-form-item .ns-form-body .ns-child-form-title {
padding-top: 0;
}
&:after {
display: none;
}
}
:deep(.ns-child-form-title) {
&:after {
content: '';
width: 75px;
height: 7px;
display: block;
background: linear-gradient(90deg, @primary-color 0%, #fff 82.67%);
margin-left: 2px;
margin-top: -7px;
}
}
}
:deep(.ns-form.ns-vertical-form) {
padding-top: 16px !important;
}
</style>

View File

@@ -0,0 +1,577 @@
<template>
<div class="ns-view">
<ns-page-header class="ns-page-header">
<template #title>
<div class="title" @click="onBack">
<!-- <left-outlined style="color: rgba(0, 0, 0, 0.4)" /> {{ title }} -->
<ns-icon name="left" /> <div class="text">{{ title }}</div>
</div>
</template>
<template #extra>
<template v-for="item in getActions" :key="item.name">
<template v-if="item.children">
<template v-if="getDropdownActions(item.children).length > 0">
<a-dropdown :trigger="['hover']" :dropMenuList="item.children">
<a class="ant-dropdown-link" @click.prevent>
{{ item.label }}
<CaretDownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item
style="padding: 2px 5px"
v-for="itemChild in getDropdownActions(item.children)"
:key="itemChild.name"
@click="itemChild.finalHandle(data, itemChild.name)">
<a-button type="link" :disabled="itemChild.dynamicDisabled">
{{ itemChild.label }}
</a-button>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</template>
<template v-else>
<ns-button
:type="item.type || 'default'"
:danger="item.danger"
:disabled="item.dynamicDisabled"
@click="item.finalHandle(data, item.name)">
<ns-v-node :content="item.label" />
</ns-button>
</template>
</template>
</template>
</ns-page-header>
<div class="ns-detail-content">
<Skeleton
active
v-for="(detailGroup, index) in getDetail"
:key="index"
:loading="loading"
:paragraph="detailGroup.SkeletonParagraphProps"
:title="SkeletonTitleProps">
<div class="ns-detail">
<a-descriptions>
<template #title>
<div class="descriptions-title" v-if="detailGroup.title">
<span>{{ detailGroup.title }}</span>
<div class="titleFloor"></div>
</div>
</template>
<template v-if="detailGroup.items">
<template v-for="(item, index) in detailGroup.items" :key="index">
<a-descriptions-item v-bind="item" v-if="item.ifShow">
<!--默认-->
<template v-if="!item.type">
<span class="ns-detail-text">{{ item.value ? item.value : '-' }}</span>
</template>
<!--富文本-->
<template v-if="item.type == 'html'">
<div class="ns-detail-html" v-html="item.value"></div>
</template>
<!--链接-->
<template v-if="item.type == 'link'">
<a class="ns-detail-link" target="_blank" :href="item.value">发票链接 </a>
</template>
<!--图片 值为数组则为图片列表 可继续扩展-->
<template v-if="item.type === 'image'">
<template v-if="typeof item.value == 'object'">
<div
class="ns-detail-image-list"
v-for="src in item.value"
:key="src"
style="display: inline-block; margin: 0 5px 5px 0">
<a-image
:width="100"
v-if="src"
:src="src"
fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==" />
</div>
</template>
<template v-else>
<a-image
class="ns-detail-image"
:width="64"
v-if="item.value"
:src="item.value"
fallback="data:image/png;base64,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" />
</template>
</template>
<!--表格-->
<template v-else-if="item.type === 'table'">
<ns-basic-table
:scroll="{ x: '100%' }"
class="ns-detail-table"
:dataSource="dataRef[item.props.listField]"
:columns="item.props.columns"
:pagination="false"
:rowKey="item.props.rowKey">
<template #bodyCell="data" v-if="!Object.keys($slots).includes('bodyCell')">
<template v-if="data.column.textEllipsis">
<!-- :style="{ width: `${data.column.width}px` }" -->
<span class="tool-tips">
<ns-tooltip
placement="top"
v-if="
data.column.customRender
? data.column.customRender(data)
: data.record[data.column.dataIndex]
">
<template #title>
<span>{{
data.column.customRender
? data.column.customRender(data)
: data.record[data.column.dataIndex] || '-'
}}</span>
</template>
<span class="text-ellipsis">{{
data.column.customRender
? data.column.customRender(data)
: data.record[data.column.dataIndex] || '-'
}}</span>
</ns-tooltip>
<span class="text-ellipsis" v-else> - </span>
</span>
</template>
<template v-if="data.column.edit">
<ns-table-cell
:value="data.text"
:record="data.record"
:column="data.column"
:index="data.index" />
</template>
</template>
</ns-basic-table>
</template>
<!--表格-->
<template v-else-if="item.type === 'NsViewListTable'">
<NsViewListTable
class="ns-detail-NsViewListTable"
v-bind="item.props.tableConfig" />
</template>
<template v-else-if="item.type === 'map'">
<NsMapV2 :value="item['value']" :viewOnly="true" />
</template>
<template v-for="slot in Object.keys($slots)" :key="slot">
<div v-if="item.type === slot" :style="item.style" :class="`ns-detail-${slot}`">
<slot :name="slot" v-bind="item || {}"> </slot>
</div>
</template>
</a-descriptions-item>
</template>
</template>
</a-descriptions>
</div>
</Skeleton>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, watch, PropType, reactive } from 'vue';
import { http } from '/nerv-lib/util/http';
import { useRoute } from 'vue-router';
import { cloneDeep, get, isFunction, isUndefined } from 'lodash-es';
import { Skeleton } from 'ant-design-vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import { Action, useAction } from '/nerv-lib/use';
import { useNavigate } from '/nerv-lib/use/use-navigate';
import { any } from 'vue-types';
export interface DetailItem {
label: string;
name: string;
value?: string;
ifShow?: Boolean | Function;
format?: Function;
type?: string; // 现支持image
class?: string;
}
export interface DetailGroup {
title: string;
items: Array<DetailItem>;
}
interface ColumnActions {
back: Boolean;
actions: Action[];
}
export default defineComponent({
name: 'NsViewDetail',
props: {
title: {
type: String,
default: '查看',
},
detail: {
type: Array as PropType<DetailGroup[]>,
default: () => [],
},
api: {
type: String,
require: true,
},
headActions: {
type: Object as PropType<ColumnActions>,
default: () => ({
actions: [],
}),
},
dataHandle: Function,
customDataApi: {
type: Function,
},
requersConfig: {
type: any,
},
customBack: {
type: String || Function,
},
},
components: { Skeleton, LeftOutlined },
setup(props) {
const { navigateBackV2: navigateBack } = useNavigate();
const SkeletonTitleProps = ref({
width: 150,
});
const route = useRoute();
const dataRef = ref();
const getData = computed(() => {
return {
...route.query,
...route.params,
dataRef: dataRef,
};
});
let loading = ref<boolean>(true);
function request() {
const { query } = route;
if (props.customDataApi) {
props.customDataApi(props.api, query).then((res) => {
dataRef.value = res.data;
loading.value = false;
});
} else {
http.get(props.api, query, props.requersConfig).then((res) => {
dataRef.value = props.dataHandle ? props.dataHandle(res.data) : res.data;
loading.value = false;
});
}
}
request();
const getDetail = computed(() => {
const detail = cloneDeep(props.detail);
return (detail as Array<DetailGroup>).map((group: DetailGroup) => {
const SkeletonWidth = [];
for (let i = 0; i < group.items.length; i++) {
if (group.items[i].type === 'table') {
SkeletonWidth.push('80%');
} else {
SkeletonWidth.push('30%');
}
}
const { title, items } = group;
return {
title: title,
items: items.map((item) => {
if (!loading.value) {
const { ifShow } = item;
item.value = get(dataRef.value, item.name);
item.ifShow = isFunction(ifShow)
? ifShow(dataRef.value)
: isUndefined(ifShow)
? true
: ifShow;
if (item.format) {
item.value = item.format(item.value, dataRef.value);
}
}
return item;
}),
SkeletonParagraphProps: {
rows: items.length,
width: SkeletonWidth,
},
SkeletonTitleProps: {
width: 150,
},
};
});
});
//支持自定义按钮
const { transformAction, filterActionNoAuth } = useAction();
const getActions = computed(() => {
let actions = cloneDeep(props.headActions.actions);
actions = actions
.filter((action: Action) => {
return filterActionNoAuth(action, getData.value);
})
.map((action) => {
return transformAction(action, getData.value);
});
return actions;
});
const getDropdownActions = (actions) => {
actions = actions.map((action) => {
return transformAction(action, getData.value);
});
return actions;
};
return {
getDetail,
loading,
dataRef,
SkeletonTitleProps,
getActions,
getDropdownActions,
navigateBack,
};
},
methods: {
onBack() {
//debugger;
if (this.customBack) {
this.customBack();
} else {
this.navigateBack();
}
// this.$router.go(-1);
},
},
});
</script>
<style lang="less" scoped>
.ns-view {
min-height: 100%;
height: 100%;
background: #e5ebf0;
}
.ns-detail-content {
border-top: 16px solid #e5ebf0;
padding: 16px 21px;
background: #fff;
height: calc(100% - 50px);
}
:deep(.ant-skeleton-paragraph) {
display: flex;
flex-wrap: wrap;
}
:deep(.ant-skeleton-paragraph li:nth-child(n)) {
display: block;
margin-right: 4%;
margin-top: 16px;
margin-bottom: 4px;
}
:deep(.ant-skeleton-paragraph li:nth-child(3n + 3)) {
margin-right: 0;
}
:deep(.ant-skeleton-content) {
padding: 0 8px 10px 10px;
}
:deep(.ant-descriptions-item-label) {
color: rgba(0, 0, 0, 0.5);
}
:deep(.ant-descriptions-item-label),
:deep(.ant-descriptions-item-content) {
line-height: 22px;
}
:deep(.ant-descriptions-view) {
padding-bottom: 8px;
}
:deep(.ant-descriptions-item) {
padding-right: 20px;
&:nth-child(2n) {
padding-left: 20px;
}
&:nth-child(3n) {
padding-left: 20px;
padding-right: 0;
}
&:last-child {
padding-right: 0;
}
}
.descriptions-title {
&:after {
content: '';
width: 75px;
height: 7px;
display: block;
background: linear-gradient(90deg, @primary-color 0%, #fff 82.67%);
margin-left: 2px;
margin-top: -2px;
}
}
.ns-page-header {
margin-bottom: 0 !important;
padding: 7px 16px !important;
width: calc(100% + 32px);
margin-left: -16px;
.title {
cursor: pointer;
font-size: 18px !important;
display: flex;
align-items: center;
.text {
margin-left: 6px;
}
}
}
.ns-detail {
border-bottom: 1px solid #ecedef;
&:last-child {
border-bottom-width: 0;
}
&:first-child {
:deep(.ant-descriptions-header) {
margin-top: 0;
}
}
:deep(.ant-descriptions-header) {
margin-top: 12px;
margin-bottom: 12px;
.ant-descriptions-title {
line-height: 16px;
font-size: 16px;
}
}
}
:deep(.ant-image) {
width: 64px;
height: 64px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.ns-detail-html {
:deep(table) {
border-top: 1px solid #ffffff;
border-left: 1px solid #ffffff;
:deep(p) {
font-size: 12px;
color: #898e91;
}
}
:deep(th) {
border-right: 1px solid #ffffff;
font-size: 13px;
padding-top: 5px;
padding-bottom: 5px;
font-weight: normal;
background: #eff0f2;
}
:deep(td) {
border-top: 1px solid #ffffff;
border-right: 1px solid #ffffff;
padding-top: 5px;
padding-bottom: 5px;
font-size: 12px;
color: #606060;
text-align: center;
:deep(text) {
border-bottom: 1px solid #ffffff;
}
background: rgba(240, 242, 245, 0.5);
}
}
.ns-detail-content {
.ns-detail-table {
:deep(.ns-basic-table) {
padding-top: 0;
.ant-table-wrapper {
width: 100%;
}
}
}
.ns-detail-NsViewListTable {
background-color: #fff !important;
:deep(.ns-table-search) {
border-top: 0;
padding-top: 0;
}
:deep(.ns-table-header) {
// margin-bottom: 16px;
padding-top: 0;
height: auto;
}
:deep(.ns-table-main) {
border-top: 0;
}
}
:deep(.ns-detail-tableConfig) {
width: 100%;
}
}
:deep(.ant-descriptions-row > td) {
padding-bottom: 8px !important;
}
:deep(.ns-detail-tablefullcontent) {
.ant-descriptions-item-content {
width: 100%;
display: block;
}
}
.text-ellipsis {
display: inline-block;
vertical-align: top;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.tool-tips {
display: inline-block;
vertical-align: top;
padding: 0;
word-wrap: break-word;
word-break: break-word;
width: 100%;
}
</style>

View File

@@ -0,0 +1,468 @@
<template>
<div class="ns-view">
<ns-page-header class="ns-page-header">
<template #title>
<div class="title" @click="onBack">
<!-- <left-outlined style="color: rgba(0, 0, 0, 0.4)" /> {{ title }} -->
<ns-icon name="left" /> <div class="text">{{ title }}</div>
</div>
</template>
<template #extra>
<template v-for="item in getActions" :key="item.name">
<template v-if="item.children">
<template v-if="getDropdownActions(item.children).length > 0">
<a-dropdown :trigger="['hover']" :dropMenuList="item.children">
<a class="ant-dropdown-link" @click.prevent>
{{ item.label }}
<CaretDownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item
style="padding: 2px 5px"
v-for="itemChild in getDropdownActions(item.children)"
:key="itemChild.name"
@click="itemChild.finalHandle(data, itemChild.name)">
<a-button type="link" :disabled="itemChild.dynamicDisabled">
{{ itemChild.label }}
</a-button>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</template>
<template v-else>
<ns-button
:type="item.type || 'default'"
:danger="item.danger"
:disabled="item.dynamicDisabled"
@click="item.finalHandle(data, item.name)">
<ns-v-node :content="item.label" />
</ns-button>
</template>
</template>
</template>
</ns-page-header>
<div class="ns-detail-content">
<Skeleton
active
v-for="(detailGroup, index) in getDetail"
:key="index"
:loading="loading"
:paragraph="detailGroup.SkeletonParagraphProps"
:title="SkeletonTitleProps">
<div class="ns-detail">
<a-descriptions>
<template #title>
<div class="descriptions-title" v-if="detailGroup.title">
{{ detailGroup.title }}
</div>
</template>
<template v-if="detailGroup.items">
<template v-for="(item, index) in detailGroup.items" :key="index">
<a-descriptions-item v-bind="item" v-if="item.ifShow">
<!--默认-->
<template v-if="!item.type">
<span class="ns-detail-text">{{ item.value ? item.value : '-' }}</span>
</template>
<!--富文本-->
<template v-if="item.type == 'html'">
<div class="ns-detail-html" v-html="item.value"></div>
</template>
<!--链接-->
<template v-if="item.type == 'link'">
<a class="ns-detail-link" target="_blank" :href="item.value">发票链接 </a>
</template>
<!--图片 值为数组则为图片列表 可继续扩展-->
<template v-if="item.type === 'image'">
<template v-if="typeof item.value == 'object'">
<div
class="ns-detail-image-list"
v-for="src in item.value"
:key="src"
style="display: inline-block; margin: 0 5px 5px 0">
<a-image
:width="100"
v-if="src"
:src="src"
fallback="data:image/png;base64,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" />
</div>
</template>
<template v-else>
<a-image
class="ns-detail-image"
:width="64"
v-if="item.value"
:src="item.value"
fallback="data:image/png;base64,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" />
</template>
</template>
<!--表格-->
<template v-else-if="item.type === 'table'">
<ns-basic-table
class="ns-detail-table"
:dataSource="dataRef[item.props.listField]"
:columns="item.props.columns"
:pagination="false"
:rowKey="item.props.rowKey" />
</template>
<!--表格-->
<template v-else-if="item.type === 'NsViewListTable'">
<NsViewListTable v-bind="item.props.tableConfig" />
</template>
<template v-else-if="item.type === 'map'">
<NsMapV2 :value="item['value']" :viewOnly="true" />
</template>
<template v-for="slot in Object.keys($slots)" :key="slot">
<div v-if="item.type === slot" :style="item.style" :class="`ns-detail-${slot}`">
<slot :name="slot" v-bind="item || {}"> </slot>
</div>
</template>
</a-descriptions-item>
</template>
</template>
</a-descriptions>
</div>
</Skeleton>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, watch, PropType, reactive } from 'vue';
import { http } from '/nerv-lib/util/http';
import { useRoute } from 'vue-router';
import { cloneDeep, get, isFunction, isUndefined } from 'lodash-es';
import { Skeleton } from 'ant-design-vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import { Action, useAction } from '/nerv-lib/use';
export interface DetailItem {
label: string;
name: string;
value?: string;
ifShow?: Boolean | Function;
format?: Function;
type?: string; // 现支持image
}
export interface DetailGroup {
title: string;
items: Array<DetailItem>;
}
interface ColumnActions {
back: Boolean;
actions: Action[];
}
export default defineComponent({
name: 'NsViewDetailV2',
props: {
title: {
type: String,
default: '查看',
},
detail: {
type: Array as PropType<DetailGroup[]>,
default: () => [],
},
api: {
type: String,
require: true,
},
headActions: {
type: Object as PropType<ColumnActions>,
default: () => ({
actions: [],
}),
},
dataHandle: Function,
},
components: { Skeleton, LeftOutlined },
setup(props) {
const SkeletonTitleProps = ref({
width: 150,
});
const route = useRoute();
const dataRef = ref();
const getData = computed(() => {
return {
...route.query,
...route.params,
dataRef: dataRef,
};
});
let loading = ref<boolean>(true);
function request() {
const { query } = route;
http.get(props.api, query).then((res) => {
// dataRef.value = res.data;
dataRef.value = props.dataHandle ? props.dataHandle(res.data) : res.data;
loading.value = false;
});
}
request();
const getDetail = computed(() => {
const detail = cloneDeep(props.detail);
return (detail as Array<DetailGroup>).map((group: DetailGroup) => {
const SkeletonWidth = [];
for (let i = 0; i < group.items.length; i++) {
if (group.items[i].type === 'table') {
SkeletonWidth.push('80%');
} else {
SkeletonWidth.push('30%');
}
}
const { title, items } = group;
return {
title: title,
items: items.map((item) => {
if (!loading.value) {
const { ifShow } = item;
item.value = get(dataRef.value, item.name);
item.ifShow = isFunction(ifShow)
? ifShow(dataRef.value)
: isUndefined(ifShow)
? true
: ifShow;
if (item.format) {
item.value = item.format(item.value, dataRef.value);
}
}
return item;
}),
SkeletonParagraphProps: {
rows: items.length,
width: SkeletonWidth,
},
SkeletonTitleProps: {
width: 150,
},
};
});
});
//支持自定义按钮
const { transformAction, filterActionNoAuth } = useAction();
const getActions = computed(() => {
let actions = cloneDeep(props.headActions.actions);
actions = actions
.filter((action: Action) => {
return filterActionNoAuth(action, getData.value);
})
.map((action) => {
return transformAction(action, getData.value);
});
return actions;
});
const getDropdownActions = (actions) => {
actions = actions.map((action) => {
return transformAction(action, getData.value);
});
return actions;
};
return {
getDetail,
loading,
dataRef,
SkeletonTitleProps,
getActions,
getDropdownActions,
};
},
methods: {
onBack() {
//debugger;
this.$router.go(-1);
},
},
});
</script>
<style lang="less" scoped>
.ns-view {
min-height: 100%;
height: 100%;
background: #e5ebf0;
}
.ns-detail-content {
border-top: 16px solid #e5ebf0;
padding: 16px 21px;
background: #fff;
height: calc(100% - 50px);
}
:deep(.ant-skeleton-paragraph) {
display: flex;
flex-wrap: wrap;
}
:deep(.ant-skeleton-paragraph li:nth-child(n)) {
display: block;
margin-right: 4%;
margin-top: 16px;
margin-bottom: 4px;
}
:deep(.ant-skeleton-paragraph li:nth-child(3n + 3)) {
margin-right: 0;
}
:deep(.ant-skeleton-content) {
padding: 0 8px 10px 10px;
}
:deep(.ant-descriptions-item-label) {
color: rgba(0, 0, 0, 0.5);
}
:deep(.ant-descriptions-item-label),
:deep(.ant-descriptions-item-content) {
line-height: 22px;
}
:deep(.ant-descriptions-view) {
padding-bottom: 8px;
}
:deep(.ant-descriptions-item) {
padding-right: 20px;
&:nth-child(2n) {
padding-left: 20px;
}
&:nth-child(3n) {
padding-left: 20px;
padding-right: 0;
}
&:last-child {
padding-right: 0;
}
}
.descriptions-title {
&:after {
content: '';
width: 75px;
height: 7px;
display: block;
background: linear-gradient(90deg, @primary-color 0%, #fff 82.67%);
margin-left: 2px;
margin-top: -2px;
}
}
.ns-page-header {
margin-bottom: 0 !important;
padding: 7px 16px !important;
width: calc(100% + 32px);
margin-left: -16px;
.title {
cursor: pointer;
font-size: 18px !important;
display: flex;
align-items: center;
.text {
margin-left: 6px;
}
}
}
.ns-detail {
border-bottom: 1px solid #ecedef;
&:last-child {
border-bottom-width: 0;
}
&:first-child {
:deep(.ant-descriptions-header) {
margin-top: 0;
}
}
:deep(.ant-descriptions-header) {
margin-top: 12px;
margin-bottom: 12px;
.ant-descriptions-title {
line-height: 16px;
font-size: 16px;
}
}
}
:deep(.ant-image) {
width: 64px;
height: 64px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.ns-detail-html {
:deep(table) {
border-top: 1px solid #ffffff;
border-left: 1px solid #ffffff;
:deep(p) {
font-size: 12px;
color: #898e91;
}
}
:deep(th) {
border-right: 1px solid #ffffff;
font-size: 13px;
padding-top: 5px;
padding-bottom: 5px;
font-weight: normal;
background: #eff0f2;
}
:deep(td) {
border-top: 1px solid #ffffff;
border-right: 1px solid #ffffff;
padding-top: 5px;
padding-bottom: 5px;
font-size: 12px;
color: #606060;
text-align: center;
:deep(text) {
border-bottom: 1px solid #ffffff;
}
background: rgba(240, 242, 245, 0.5);
}
}
.ns-detail-content {
:deep(.ns-basic-table) {
padding-top: 0;
.ant-table-wrapper {
width: 100%;
}
}
:deep(.ns-detail-tableConfig) {
width: 100%;
}
}
:deep(.ant-descriptions-row > td) {
padding-bottom: 8px !important;
}
</style>

View File

@@ -0,0 +1,101 @@
<template>
<div :key="'edieForm_' + $route.name" class="ns-view">
<ns-page-header class="ns-page-header" :sticky="sticky" :title="title">
<template #extra>
<!-- todo 隐藏取消-->
<a-button type="primary" @click="submit" :disabled="!nsFormElRef?.validateResult"
>提交</a-button
>
<a-button style="margin-left: 10px" @click="navigateBack">返回</a-button>
</template>
</ns-page-header>
<div class="ns-add-form">
<ns-form ref="nsFormElRef" v-bind="getBindValue" :model="data"
><template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot> </template
></ns-form>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from 'vue';
import { NsMessage } from '/nerv-lib/component/message';
import { useRoute } from 'vue-router';
import { formProps } from '/nerv-lib/component/form/form/props';
import { PropTypes } from '/nerv-lib/util/type';
import { useApi, HttpRequestConfig } from '/nerv-lib/use/use-api';
import { useNavigate } from '/nerv-lib/use/use-navigate';
export default defineComponent({
name: 'NsViewEditForm',
props: {
...formProps,
api: PropTypes.string,
getApi: PropTypes.string,
title: PropTypes.string,
},
setup(props, { attrs }) {
const nsFormElRef = ref();
const { httpRequest } = useApi();
const route = useRoute();
const { navigateBack } = useNavigate();
const data = ref<Recordable>({});
const request = () => {
const { api, getApi } = props;
const { params, query } = route;
const requestConfig: HttpRequestConfig = { method: 'get' };
httpRequest({
api: getApi ? getApi : api,
params: query,
pathParams: params,
requestConfig,
}).then((res: Recordable) => {
data.value = res;
});
};
request();
function submit() {
nsFormElRef.value
.triggerSubmit()
.then((data: Recordable) => {
const { api } = props;
const { params } = route;
const requestConfig: HttpRequestConfig = { method: 'PUT' };
httpRequest({ api, params: data, pathParams: params, requestConfig }).then(() => {
NsMessage.success('操作成功', 1, () => {
navigateBack();
});
});
})
.catch(() => ({}));
}
const getBindValue = computed(() => ({
...attrs,
...props,
}));
return { nsFormElRef, submit, data, getBindValue, navigateBack };
},
});
</script>
<style lang="less" scoped>
:deep(.ns-page-header) {
margin-bottom: 0 !important;
}
.ns-add-form {
:deep(.ns-form) {
padding: 32px 24px;
// 第一个子表单Title距离顶部为0
&.ns-flex-form-vertical
.ns-form-body:first-child
.ns-form-item:first-child
.ns-form-body
.ns-child-form-title {
padding-top: 0;
}
}
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<div class="ns-view">
<a-layout class="ns-application" style="height: 100%">
<ns-header class="ns-header-menu" />
<a-layout>
<ns-sider class="ns-left-menu" />
<a-layout>
<a-layout-content class="content" :style="{ minHeight: '400px', overflow: 'auto' }">
<div>
<ns-icon name="noResource" size="250" />
</div>
<!-- <img src="/asset/image/noResource.png" /> -->
</a-layout-content>
</a-layout>
</a-layout>
</a-layout>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import NsHeader from '/nerv-base/view/system/layout/header.vue';
import NsSider from '/nerv-base/view/system/layout/sider.vue';
export default defineComponent({
name: 'Error403View',
components: { NsHeader, NsSider },
setup() {
return {};
},
created() {},
});
</script>
<style lang="less" scoped>
.application {
display: flex;
width: 100%;
height: 100%;
min-height: 100%;
background-color: #f4f7f9;
flex-direction: column;
}
.content {
height: calc(100% - 64px);
display: flex;
align-items: center;
justify-content: center;
img {
width: 240px;
height: 240px;
}
}
</style>

View File

@@ -0,0 +1,143 @@
<!-- @format -->
<template>
<div class="ns-list-table ns-view">
<ns-table ref="nsTableRef" v-bind="getBindValue">
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data"></slot>
</template>
</ns-table>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, unref, nextTick } from 'vue';
import { tableProps } from '/nerv-lib/component/table/props';
import { PropTypes } from '/nerv-lib/util/type';
import { cloneDeep, get, isArray } from 'lodash-es';
tableProps.expand = PropTypes.bool.def(false);
export default defineComponent({
name: 'NsViewListTable',
props: {
...tableProps,
title: PropTypes.string,
},
setup(props, { attrs }) {
const nsTableRef = ref();
const getPageParams = (dataRef, curepage?) => {
let pageCount = get(dataRef.value, props.pageCountField);
let pageSize = get(dataRef.value, props.sizeField);
let total = 0;
let page = 0;
const tableData = get(unref(dataRef), props.listField);
if (pageCount) {
let totalMax = pageCount * pageSize;
if (get(dataRef.value, props.totalField)) {
total =
totalMax > get(dataRef.value, props.totalField)
? get(dataRef.value, props.totalField)
: totalMax;
} else {
total = totalMax;
}
} else {
total = get(dataRef.value, props.totalField);
}
if (isArray(tableData) ? !tableData.length : false && curepage) {
//后端从0开始
if (curepage !== 0) {
page =
curepage > pageCount - props.pageFieldOffset
? pageCount - props.pageFieldOffset
: curepage;
}
} else {
page = curepage;
}
return { total, page };
};
const formConfig = cloneDeep(props.formConfig);
const getFormConfig = computed(() => {
const formConfig = cloneDeep(props.formConfig);
if (formConfig) {
formConfig['formLayout'] = 'flexv2';
}
return formConfig;
});
const getBindValue = computed(() => ({
...attrs,
...props,
title: props.tableTitle,
tableTitle: props.title,
getPageParams: getPageParams,
formConfig: getFormConfig.value,
sticky:
props.sticky === false
? false
: { offsetHeader: props.headerActions.length === 0 && !props.title ? 0 : 89 },
}));
if (formConfig) {
nextTick(() => {
unref(nsTableRef)?.formElRef?.triggerSubmit();
});
}
return { getBindValue, nsTableRef };
},
//缓存界面重新调用reload
activated() {
if (unref(this.nsTableRef)) {
unref(this.nsTableRef).reload();
}
},
});
</script>
<style lang="less" scoped>
//关闭 sticky 模式 底部滚动条
:deep(.ns-table) {
.ns-table-main {
padding: 0px 16px;
border-top: 16px solid #e5ebf0;
// margin: 16px;
// background-color: #fff;
}
.ns-table-search {
padding: 16px 16px 0;
// margin: 16px;
background: #fff;
// border-width: 16px 0 16px 0px;
// border-color: #e5ebf0;
// border-style: solid;
border-top: 16px solid #e5ebf0;
}
.ns-table-header {
position: sticky;
z-index: 3;
top: 0;
left: 0;
background-color: @white;
}
.ant-table-sticky-scroll {
display: none !important;
}
.ns-basic-table {
padding-top: 16px;
}
}
:deep(.ns-form::after) {
background-color: #f0f2f5;
display: none;
margin: 0 16px;
width: calc(100% - 32px);
}
</style>

View File

@@ -0,0 +1,434 @@
<!-- @format -->
<template>
<div class="signUp ns-view">
<a-layout>
<a-layout-header class="home_header" style="position: relative">
<img
v-if="themeConfig.logoUrl"
:src="themeConfig.logoUrl"
style="width: 192px; height: 48px; object-fit: contain" />
<ns-icon v-else name="login" style="width: auto; height: 40px" />
</a-layout-header>
<a-layout-content>
<div class="content">
<h1>{{ title }}</h1>
<div class="lg_card">
<h2>{{ themeConfig.webSiteTitle ? themeConfig.webSiteTitle : subTitle }}</h2>
<div class="form">
<ns-form
style="width: 100%; margin: auto"
ref="formOneRef"
formLayout="修改"
:schemas="formSchema"
:model="data" />
<div class="step-box">
<a-button @click="navigateBack">取消</a-button>
<a-button @click="submit" type="primary" :disabled="!formOneRef?.validateResult"
>确认修改</a-button
>
</div>
</div>
</div>
</div>
</a-layout-content>
<a class="toLogin" @click="toLogin" v-show="step === 0">登录已有豪恩账号 ></a>
<!-- <a-layout-footer>Copyright 2021 鼎云科技 All Rights Reserved</a-layout-footer> -->
</a-layout>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { http } from '/nerv-lib/util/http';
import { Cookies } from '/nerv-lib/util/cookie';
import { authorizationService } from '/nerv-base/store/modules/authorization-service';
import { NsMessage } from '/nerv-lib/component/message';
import { appConfigStore } from '/nerv-lib/saas/store/modules/app-config';
import { storeToRefs } from 'pinia';
import { useNavigate } from '/nerv-lib/use/use-navigate';
export default defineComponent({
name: 'UpdatePassWord',
setup() {
const router = useRouter();
const imageUrl = ref('/asset/image/login/logo.png');
const title = ref('修改密码');
const subTitle = ref('sass平台');
const { uuid } = router.currentRoute.value.query;
const formOneRef = ref();
const configStore = appConfigStore();
const { getThemeConfig: themeConfig } = storeToRefs(configStore);
if (configStore.updatePassWordInfo.title) {
title.value = configStore.updatePassWordInfo.title;
subTitle.value = configStore.updatePassWordInfo.subtitle;
}
let data = reactive({});
const authorizationStore = authorizationService();
const { navigateBackV2: navigateBack } = useNavigate();
const formSchema = reactive([
{
field: 'oldPassword',
label: '原密码',
component: 'NsInputPassword',
componentProps: {
autocomplete: 'new-password',
placeholder: '请输入原密码',
},
rules: [
{
required: true,
message: '原密码不能为空',
trigger: 'blur',
},
],
},
{
field: 'newPassword',
label: '新密码',
component: 'NsInputPassword',
componentProps: {
autocomplete: 'new-password',
placeholder: '密码不少于6位支持字母、数字',
onchange: () => {
data.confirmPassword = '';
},
},
rules: [
{
required: true,
message: '新密码不能为空',
trigger: 'change',
},
{
validator: async (rule, value) => {
if (!value) {
return;
}
if (!/^([a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*])[a-z\d!@#$%^&*]{5,17}$/.test(value)) {
let strArray = value.split('');
if (strArray.indexOf(' ') !== -1) {
return Promise.reject('不能包含空格');
} else {
if (/[\u4E00-\u9FA5\uF900-\uFA2D]{1,}/.test(value)) {
return Promise.reject('不支持中文');
} else {
return Promise.reject(
'需同时包含数字,字母及特殊字符(!@#$%^&*)且只能以字母开头长度为6-18个字符',
);
}
}
}
},
trigger: 'change',
},
],
},
{
field: 'confirmPassword',
label: '确认密码',
component: 'NsInputPassword',
viewOnly: true,
componentProps: {
autocomplete: 'new-password',
placeholder: '请再次输入密码',
},
rules: [
{
required: true,
message: '不能为空',
trigger: 'change',
},
{
trigger: 'change',
validator: async (rule, value) => {
if (!value) {
return;
}
if (value !== data.newPassword) {
return Promise.reject('两次密码不一致');
}
},
},
],
},
]);
const loading = ref<boolean>(false);
// const title = ref<string>('修改密码');
const submit = (): void => {
formOneRef.value.triggerSubmit().then((data) => {
loading.value = true;
async function update() {
try {
const res = await http.post(configStore.updatePassWordInfo?.api, data);
if (res.success) {
NsMessage.success('修改成功,需重新登陆', 1, () => {
Cookies.remove('nervsid');
sessionStorage.clear();
router.push('/login');
authorizationStore.clearAuthorization();
});
}
loading.value = false;
} catch (err) {
loading.value = false;
}
}
update();
});
};
return {
themeConfig,
submit,
formSchema,
data,
subTitle,
formOneRef,
imageUrl,
router,
uuid,
navigateBack,
title,
loading,
};
},
created() {
const _this = this;
window.sessionStorage.clear();
document.onkeydown = function (e) {
const key = window.event === undefined ? e.keyCode : window.event.keyCode;
key === 13 ? _this.submit() : '';
};
},
beforeUnmount() {
document.onkeydown = function (e) {};
},
});
</script>
<style lang="less" scoped>
.ns-form.ns-vertical-form {
width: 80% !important;
}
.ns-form::after {
background-color: #f0f2f5;
display: none;
margin: 0 16px;
width: calc(100% - 32px);
}
.finalSubmitDiv {
width: 100%;
margin-top: 45px;
display: flex;
align-items: center;
justify-content: center;
.finalSubmit {
width: 90px;
height: 32px;
}
}
:deep(.verifyImgCode .ant-form-item) {
margin-bottom: 12px !important;
}
.signUp {
height: 100%;
overflow: hidden;
}
.content {
width: 100%;
height: 100%;
padding-top: 40px;
h1 {
text-align: center;
font-size: 24px;
line-height: 44px;
margin-bottom: 40px;
}
.lg_card {
width: 80%;
min-width: 800px;
height: 70%;
min-height: 400px;
background: #fff;
// box-shadow: 0 2px 2px 0 rgb(0 0 0 / 5%), 0 0 18px 0 rgb(0 0 0 / 8%);
border-radius: 6px;
margin: auto !important;
// padding: 15px 35px 35px;
.form {
width: 50%;
min-width: 400px;
margin: auto;
padding-top: 40px;
}
h2 {
padding-top: 24px;
padding-bottom: 24px;
font-size: 16px;
text-align: center;
border-bottom: 1px solid rgba(23, 46, 61, 0.15);
}
}
}
.toLogin {
width: 100%;
font-size: 16px;
line-height: 22px;
text-align: center;
color: #3aa7f2;
display: block;
margin-top: 40px;
margin-bottom: 16px;
}
.icon {
color: @primary-color;
// background: #000;
// min-height: 25px;
// min-width: 25px;
// display: flex;
// justify-content: center;
// align-items: center;
// border-radius: 50%;
}
.ant-layout-header {
height: 64px !important;
background: #fff;
line-height: 64px;
display: flex;
align-items: center;
}
.ant-layout-content {
border-top: 1px solid #ecedef;
width: 100%;
display: flex;
// align-items: center;
justify-content: center;
height: calc(100vh - 128px) !important;
}
.ant-layout-footer {
text-align: center;
font-size: 14px;
font-weight: 400;
color: #8f8f8f;
}
.lg_card_title {
width: 360px;
margin: 60px auto 0;
height: 48px;
font-size: 20px;
font-weight: bold;
color: #172e3d;
text-align: left;
line-height: 20px;
span {
float: right;
font-size: 14px;
color: #808d96;
font-weight: lighter;
strong {
color: #1d2d3d;
font-weight: 400;
display: inline-block;
cursor: pointer;
&:hover {
color: #ff1744;
}
}
}
}
:deep(.ant-form-item) {
margin-bottom: 24px;
}
:deep(#app .ant-form-item-label > label) {
color: #606060;
}
:deep(.lg_card .ant-col-5) {
width: 30%;
flex: 0 0 30%;
text-align: right;
}
:deep(.ant-layout-content) {
margin: 0;
}
.success-box {
width: 100%;
text-align: center;
padding: 50px 0 4px;
h3 {
margin-top: 30px;
font-weight: bold;
margin-bottom: 24px;
}
h3,
span {
color: #ff4919;
}
p {
margin: auto;
font-size: 14px;
text-align: left;
color: #1d2d3d;
width: 50%;
white-space: nowrap;
}
}
:deep(.step-box) {
width: 80%;
display: flex;
justify-content: flex-end;
// margin-right: 50px;
position: relative;
left: 20px;
button {
margin-left: 16px;
}
}
:deep(.ant-steps-item-title::after) {
background: transparent !important;
border-top-width: 1px;
border-top-style: dashed;
border-top-color: #ecedef;
}
:deep(.ant-steps-item-process
> .ant-steps-item-container
> .ant-steps-item-content
> .ant-steps-item-title::after) {
border-top-color: #ff4919;
}
:deep(.ant-layout-header) {
max-height: 64px !important;
min-height: 64px !important;
}
:deep(.ant-layout-footer) {
max-height: 64px !important;
min-height: 64px !important;
height: 64px !important;
}
.home_header {
height: 64px;
display: flex;
align-items: center;
// img {
// height: 32px !important;
// width: 220px !important;
// transform: translateY(0px) translateX(0px);
// }
}
:deep(.ant-layout) {
height: 100%;
width: 100%;
}
</style>

View File

@@ -0,0 +1,134 @@
<template>
<div class="ns-view">
<a-spin :spinning="isLoading">
<a-page-header class="ns-page-header" :title="title">
<template #extra>
<!-- todo 隐藏取消-->
<ns-button @click="navigateBack"> </ns-button>
<ns-button
style="margin-left: 10px"
type="primary"
@click="submit"
:disabled="!nsFormElRef?.validateResult"
> </ns-button
>
</template>
</a-page-header>
<div class="ns-add-form">
<ns-form ref="nsFormElRef" v-bind="getBindValue" :model="formModel">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</ns-form>
</div>
</a-spin>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, watch } from 'vue';
import { http } from '/nerv-lib/util/http';
import { NsMessage } from '/nerv-lib/component/message';
import { formProps } from '/nerv-lib/component/form/form/props';
import { PropTypes } from '/nerv-lib/util/type';
import { useNavigate } from '/nerv-lib/use/use-navigate';
import { HttpRequestConfig, useApi } from '/nerv-lib/use/use-api';
import { useRoute } from 'vue-router';
export default defineComponent({
name: 'NsViewForm',
props: {
...formProps,
api: PropTypes.string,
editApi: PropTypes.string,
title: PropTypes.string,
isEdit: {
type: Boolean,
default: false,
},
},
setup(props, { attrs }) {
const nsFormElRef = ref();
const { httpRequest } = useApi();
const isLoading = ref(false);
const { navigateBack } = useNavigate();
const route = useRoute();
const formModel = ref<Recordable>(props.model || {});
const request = () => {
const { api, editApi } = props;
const { params, query } = route;
const requestConfig: HttpRequestConfig = { method: 'get' };
httpRequest({
api: editApi ? editApi : api,
params: query,
pathParams: params,
requestConfig,
}).then((res: Recordable) => {
formModel.value = res.data;
});
};
watch(
[() => props.api, () => props.editApi],
() => {
console.log('editApi', props.editApi);
request();
},
{
immediate: true,
},
);
const getBindValue = computed(() => ({
...attrs,
...props,
}));
function submit() {
nsFormElRef.value
.triggerSubmit()
.then((data: any) => {
isLoading.value = true;
http
.post(props.api, data)
.then(() => {
isLoading.value = false;
NsMessage.success('操作成功', 1, () => {
navigateBack();
});
})
.catch(() => {
isLoading.value = false;
});
})
.catch(() => {});
}
return { nsFormElRef, submit, getBindValue, isLoading, navigateBack, formModel };
},
});
</script>
<style lang="less" scoped>
//.ns-page-header {
// margin: 0px 16px 20px 0;
// border-bottom: 1px solid #f0f2f5;
//}
.ns-add-form .ns-vertical-form {
max-width: 600px;
margin: 24px auto;
}
:deep(.ant-spin-nested-loading) {
height: 100%;
}
.ant-spin-nested-loading {
height: 100%;
}
:deep(.ant-spin-container) {
height: 100%;
overflow: hidden;
}
:deep(.ns-add-form) {
height: calc(100% - 83px) !important;
overflow: auto;
}
</style>