Merge remote-tracking branch 'origin/main'

This commit is contained in:
luozhun 2024-11-07 17:14:19 +08:00
commit 2b3cca4a77
37 changed files with 772 additions and 268 deletions

View File

@ -1,3 +1,7 @@
# 配置文档参考 https://taro-docs.jd.com/docs/next/env-mode-config
TARO_APP_ID="wx0acd1c4fcf94bdd3"
TARO_APP_BASE_API="https://www.hnjinglian.cn:5678"
TARO_APP_BASE_API="http://172.10.10.93:8765"
# minio
TARO_APP_MINIO_URL=http://118.253.177.137:9000
TARO_APP_MINIO_BUCKET=police-security-dev

View File

@ -1,2 +1,6 @@
# TARO_APP_ID="wx0acd1c4fcf94bdd3"
TARO_APP_BASE_API="https://www.hnjinglian.cn:5678"
# minio
TARO_APP_MINIO_URL=https://www.hnjinglian.cn:9002
TARO_APP_MINIO_BUCKET=police-security

View File

@ -1 +0,0 @@
# TARO_APP_ID="测试环境下的小程序appid"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -20,7 +20,7 @@ export const MINI_PROGRAM_USER_CONFIG: Record<MiniProgramUserIdentity, UserConfi
},
{
pagePath: 'pages/police/mine/index',
text: '警察我的',
text: '我的',
iconPath: "assets/mine/my.png",
selectedIconPath: "assets/mine/my-active.png"
},
@ -37,7 +37,7 @@ export const MINI_PROGRAM_USER_CONFIG: Record<MiniProgramUserIdentity, UserConfi
},
{
pagePath: 'pages/projectManager/mine/index',
text: '项目经理我的',
text: '我的',
iconPath: "assets/mine/my.png",
selectedIconPath: "assets/mine/my-active.png"
},

View File

@ -1,47 +1,76 @@
.nameTitle {
position: absolute;
top: 277rpx;
left: 55px;
height: 125rpx;
background-color: #fff;
width: 650rpx;
border-radius: 12rpx;
box-shadow: 0px 10px 10px -4px #e3e3e3;
display: flex;
justify-content: space-between;
align-items: center;
.itemSchool {
border-right: solid 1.5rpx #dadada;
display: flex;
flex-direction: column;
align-items: center;
width: 25%;
font-size: 24px
}
.itemSchool text:nth-child(1) {
margin-bottom: 10rpx;
color: #898a8a;
margin-left: -8px;
}
}
.nameTitle .itemSchool:nth-child(4) {
border-right: none;
}
.swiperDemoItem {
color: #3886d0;
display: flex;
padding: 20px 0 0 50px;
overflow: hidden;
height: 70rpx;
margin-top: 70px;
.swiperDemoIndex {
width: 15px;
height: 45px;
background-image: linear-gradient(to bottom, #5d9cf9, #317ad9);
//background: rgb();
border-radius: 20px;
margin-right: 15px;
}
}
.subModule {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-right: -3px;
margin-left: -1px;
.subModuleItem {
width: 33%;
height: 180rpx;
.Module {
background-color: #fff;
overflow: hidden;
.subModule {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
border: 1px solid #ccc;
border-left: 0;
margin-top: -1px;
.subModuleIndex {
width: 65rpx;
height: 65rpx;
image {
width: 100%;
height: 100%;
flex-wrap: wrap;
.subModuleItem {
width: 246rpx;
height: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
.subModuleIndex {
width: 45rpx;
height: 45rpx;
image {
width: 100%;
height: 100%;
}
}
}
}
}

View File

@ -0,0 +1,47 @@
.swiperDemoItem {
color: #3886d0;
display: flex;
padding: 20px 0 0 50px;
overflow: hidden;
height: 70rpx;
.swiperDemoIndex {
width: 15px;
height: 45px;
background-image: linear-gradient(to bottom, #5d9cf9, #317ad9);
//background: rgb();
border-radius: 20px;
margin-right: 15px;
}
}
.subModule {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-right: -3px;
margin-left: -1px;
.subModuleItem {
width: 33%;
height: 180rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
border: 1px solid #ccc;
border-left: 0;
margin-top: -1px;
.subModuleIndex {
width: 65rpx;
height: 65rpx;
image {
width: 100%;
height: 100%;
}
}
}
}

View File

@ -3,56 +3,119 @@
<view class="swiperDemo">
<nut-swiper ref="swiperRef" pagination-visible pagination-color="#e53e31" :auto-play="3000" :init-page="0">
<nut-swiper-item v-for="(item, index) in list" :key="index" style="height: 180px">
<image src="@/assets/images/01.png" alt="" style="height: 100%; width: 100%" draggable="false"/>
<image :src="item" alt="" style="height: 100%; width: 100%" draggable="false"/>
<view>1123</view>
</nut-swiper-item>
</nut-swiper>
</view>
<view class="nameTitle">
<view class="itemSchool" >
<text>单位数量</text>
<text
style=" white-space: nowrap;overflow: hidden;text-overflow: ellipsis;width: 80px;text-align: center">{{numberStatistics.enterprisesUnitCount}}</text>
</view>
<view class="itemSchool">
<text>服务项目</text>
<text>
{{numberStatistics.serviceProjectCount}}
</text>
</view>
<view class="itemSchool">
<text>有保安证人员</text>
<text>{{numberStatistics.securityUserCount}}</text>
</view>
<view class="itemSchool">
<text>无保安证人员</text>
<text>{{numberStatistics.noCardSecurityUserCount}}</text>
</view>
</view>
<view class="swiperDemoItem">
<view class="swiperDemoIndex"></view>
<view>请选择</view>
</view>
<!--九宫格-->
<view>
<view class="subModule">
<view class="subModuleItem" v-for="item in subModuleList" :key="item.id" @click="subNavigation(item.url)">
<view class="Module">
<view class="subModule" >
<view class="subModuleItem" v-for="item in subModuleList" :key="item.id"
@click="subNavigation(item.url)">
<view class="subModuleIndex">
<image :src="item.icon"></image>
</view>
<view style=" font-size: 12px;color: #414141;margin-top: 9px">{{ item.name }}</view>
<view style=" font-size: 12px;color: #414141;margin-top: 5px">{{item.name}}</view>
</view>
</view>
</view>
<view style="background-color: #e9eef4;height: 15rpx"></view>
</view>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import {onMounted, ref} from 'vue'
import Taro from "@tarojs/taro";
import icon from '@/assets/images/project.png'
import icon01 from '@/assets/images/回单.jpg'
import icon02 from '@/assets/images/工单.jpg'
import icon03 from '@/assets/images/排名.jpg'
import icon04 from '@/assets/images/法制宣传.jpg'
import icon06 from '@/assets/images/警保风采.jpg'
import './index.scss'
const list = ref(['https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',])
import api from "@/request";
import {DataStatisticsRes} from "@/types/pages/police";
const list = ref([process.env.TARO_APP_MINIO_URL + '/police-security/2024/11/5/dunpai.jpg'])
const swiperRef = ref() //
const subModuleList = ref([
{
id: 0,
icon: icon,
name: '企事业单位',
name: '项目管理',
url: '/subPages/police/myEnterprisesUnit/myEnterprisesUnit'
},
{
id: 1,
icon: icon,
name: '警保风采',
icon: icon02,
name: '整改工单',
url: ''
},
{
id: 2,
icon: icon,
name: '待定',
icon: icon03,
name: '考核排名',
url: ''
},
{
id: 3,
icon: icon06,
name: '警保风采',
url: ''
},
{
id: 4,
icon: icon04,
name: '法制宣传',
url: ''
},
{
id: 5,
icon: icon01,
name: '整改回单',
url: ''
}
])
const numberStatistics = ref<DataStatisticsRes>({
enterprisesUnitCount:0,
serviceProjectCount:0,
securityUserCount:0,
noCardSecurityUserCount:0
})
const dataStatistics = async ()=>{
const resp = await api.get<DataStatisticsRes>('/policeIndex/dataStatistics')
numberStatistics.value = resp.data as DataStatisticsRes
}
onMounted(async()=>{
await dataStatistics()
})
const subNavigation = async (url: string) => Taro.navigateTo({url})
</script>

View File

@ -0,0 +1,57 @@
<template>
<view>
<view class="swiperDemo">
<nut-swiper ref="swiperRef" pagination-visible pagination-color="#e53e31" :auto-play="3000" :init-page="0">
<nut-swiper-item v-for="(item, index) in list" :key="index" style="height: 200px">
<image :src="item" alt="" style="height: 100%; width: 100%" draggable="false"/>
</nut-swiper-item>
</nut-swiper>
</view>
<view class="swiperDemoItem">
<view class="swiperDemoIndex"></view>
<view>请选择</view>
</view>
<!--九宫格-->
<view>
<view class="subModule">
<view class="subModuleItem" v-for="item in subModuleList" :key="item.id" @click="subNavigation(item.url)">
<view class="subModuleIndex">
<image :src="item.icon"></image>
</view>
<view style=" font-size: 12px;color: #414141;margin-top: 9px">{{ item.name }}</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import Taro from "@tarojs/taro";
import icon from '@/assets/images/project.png'
import './index.scss'
const list = ref([process.env.TARO_APP_MINIO_URL + '/police-security/2024/11/5/dunpai.jpg',process.env.TARO_APP_MINIO_URL + '/police-security/2024/11/5/xingqiu.jpg'])
const swiperRef = ref() //
const subModuleList = ref([
{
id: 0,
icon: icon,
name: '企事业单位',
url: '/subPages/police/myEnterprisesUnit/myEnterprisesUnit'
},
{
id: 1,
icon: icon,
name: '警保风采',
url: ''
},
{
id: 2,
icon: icon,
name: '待定',
url: ''
}
])
const subNavigation = async (url: string) => Taro.navigateTo({url})
</script>

View File

@ -1,48 +1,73 @@
.nameTitle {
position: absolute;
top: 277rpx;
left: 55px;
height: 125rpx;
background-color: #fff;
width: 650rpx;
border-radius: 12rpx;
box-shadow: 0px 10px 10px -4px #e3e3e3;
display: flex;
justify-content: space-between;
align-items: center;
.itemSchool {
border-right: solid 1.5rpx #dadada;
display: flex;
flex-direction: column;
align-items: center;
width: 25%;
font-size: 24px
}
.itemSchool text:nth-child(1) {
margin-bottom: 10rpx;
color: #898a8a;
margin-left: -8px;
}
}
.swiperDemoItem {
color: #3886d0;
display: flex;
padding: 20px 0 0 50px;
overflow: hidden;
height: 70rpx;
margin-top: 70px;
.swiperDemoIndex {
width: 15px;
height: 45px;
background-image: linear-gradient(to bottom, #5d9cf9, #317ad9);
//background: rgb();
border-radius: 20px;
margin-right: 15px;
}
}
.subModule {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-right: -3px;
margin-left: -1px;
.subModuleItem {
width: 33%;
height: 180rpx;
.Module {
background-color: #fff;
overflow: hidden;
.subModule {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
border: 1px solid #ccc;
border-left: 0;
margin-top: -1px;
.subModuleIndex {
width: 65rpx;
height: 65rpx;
image {
width: 100%;
height: 100%;
flex-wrap: wrap;
.subModuleItem {
width: 246rpx;
height: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
.subModuleIndex {
width: 45rpx;
height: 45rpx;
image {
width: 100%;
height: 100%;
}
}
}
}
}

View File

@ -4,51 +4,99 @@
<nut-swiper ref="swiperRef" pagination-visible pagination-color="#e53e31" :auto-play="3000" :init-page="0">
<nut-swiper-item v-for="(item, index) in list" :key="index" style="height: 180px">
<image :src="item" alt="" style="height: 100%; width: 100%" draggable="false"/>
<view>1123</view>
</nut-swiper-item>
</nut-swiper>
</view>
<view class="nameTitle">
<view class="itemSchool" >
<text>单位数量</text>
<text
style=" white-space: nowrap;overflow: hidden;text-overflow: ellipsis;width: 80px;text-align: center">51</text>
</view>
<view class="itemSchool">
<text>服务项目</text>
<text>
13123
</text>
</view>
<view class="itemSchool">
<text>有保安证人员</text>
<text> 1000</text>
</view>
<view class="itemSchool">
<text>无保安证人员</text>
<text> 140</text>
</view>
</view>
<view class="swiperDemoItem">
<view class="swiperDemoIndex"></view>
<view>请选择</view>
</view>
<!--九宫格-->
<view>
<view class="subModule">
<view class="subModuleItem" v-for="item in subModuleList" :key="item.id" @click="subNavigation(item.url)">
<view class="Module">
<view class="subModule" >
<view class="subModuleItem" v-for="item in subModuleList" :key="item.id"
@click="subNavigation(item.url)">
<view class="subModuleIndex">
<image :src="item.icon"></image>
</view>
<view style=" font-size: 12px;color: #414141;margin-top: 9px">{{ item.name }}</view>
<view style=" font-size: 12px;color: #414141;margin-top: 5px">{{item.name}}</view>
</view>
</view>
</view>
<view style="background-color: #e9eef4;height: 15rpx"></view>
</view>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import Taro from "@tarojs/taro";
import icon from '@/assets/images/project.png'
import './index.scss'
import icon01 from '@/assets/images/回单.jpg'
import icon02 from '@/assets/images/工单.jpg'
import icon03 from '@/assets/images/排名.jpg'
import icon04 from '@/assets/images/法制宣传.jpg'
import icon06 from '@/assets/images/警保风采.jpg'
const list = ref(['/assets/images/01.png',])
import './index.scss'
const list = ref([process.env.TARO_APP_MINIO_URL + '/police-security/2024/11/5/dunpai.jpg'])
const swiperRef = ref() //
const subModuleList = ref([
{
id: 0,
icon: icon,
name: '我的项目',
name: '项目管理',
url: '/subPages/projectManager/myProject/myProject'
},
{
id: 1,
icon: icon,
name: '警保风采',
icon: icon02,
name: '整改工单',
url: ''
},
{
id: 2,
icon: icon,
name: '待定',
icon: icon03,
name: '考核排名',
url: ''
},
{
id: 3,
icon: icon06,
name: '警保风采',
url: ''
},
{
id: 4,
icon: icon04,
name: '法制宣传',
url: ''
},
{
id: 5,
icon: icon01,
name: '整改回单',
url: ''
}
])

View File

@ -7,8 +7,8 @@
<view class="tips-text">
<view style="font-size: 15px;">名字</view>
<view style="font-size: 12px;">
<text style="margin-right: 5px">教育部门 </text>
<text>未选择学校</text>
<text style="margin-right: 5px">保安部门 </text>
<text>未选择单位</text>
</view>
</view>
</view>

View File

@ -33,13 +33,7 @@
</nut-radio>
</nut-radio-group>
</nut-form-item>
<nut-form-item label="手机号:">
<nut-input
v-model="formData.telephone"
placeholder="请输入手机号码"
type="text"
/>
</nut-form-item>
<nut-form-item label="行政区划:" prop="name">
<view @click="visible = true" style="color: #808080">
{{ streetCommunitySmallCommunityLabel || "请选择行政区划" }}
@ -55,8 +49,9 @@
v-model:visible="visible"
v-model="TreeValue"
title="选择地址"
:options="TreeData"
@change="change"
lazy
:lazy-load="lazyLoad"
text-key="label"
></nut-cascader>
<nut-popup v-model:visible="show" position="bottom">
@ -95,18 +90,28 @@ const formData = ref<RegisterParams>({
});
const show = ref(false)
const visible = ref(false)
const TreeValue = ref<Record<string, any>[]>([])
const TreeData = ref(['']);
// const TreeData = ref([]);
const streetCommunitySmallCommunityLabel = ref<string>("");
const getAdministrativeDivisionTree = async () => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionTree')
TreeData.value = resp.data as any
console.log(resp.data)
const getAdministrativeDivisionTree = async (value:string) => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:value})
return resp.data as any
}
const change = (value: string, pathNodes: Record<string, any>[]) => {
const change = async (value: string, pathNodes: Record<string, any>[]) => {
streetCommunitySmallCommunityLabel.value = pathNodes.map((e) => e.text).join(",");
TreeValue.value = value as any
}
const lazyLoad = async (node:any, resolve:any)=>{
if (node.root) {
await resolve(getAdministrativeDivisionTree ('0'))
} else {
await resolve(getAdministrativeDivisionTree (node.value))
}
}
const columns = ref([])
const unitsList = async () => {
if (streetCommunitySmallCommunityLabel.value !== '') {
@ -126,7 +131,7 @@ const unitsList = async () => {
const selectedLabel = ref('')
const confirm = ({selectedOptions, selectedValue}) => {
const confirm = ({selectedOptions}) => {
Object.keys(selectedOptions).forEach((e) => {
selectedLabel.value = selectedOptions[e].label
formData.value.unitId = selectedOptions[e].value
@ -136,7 +141,7 @@ const confirm = ({selectedOptions, selectedValue}) => {
}
//
watch(() => formData.value.identity, (value) => {
watch(() => formData.value.identity, () => {
formData.value.unitId = ''
selectedLabel.value = ''
})
@ -165,7 +170,7 @@ const register = async () => {
await Taro.setStorage({
key: "token",
data: resp.data,
success(res) {
success() {
Taro.navigateTo({
url: '/pages/login/login'
})
@ -209,10 +214,10 @@ const onChooseAvatar = (e) => {
formData.value.avatar = avatarUrl
}
//
const getNickname = (e) => {
formData.value.name = e.detail.value
console.log(formData.value.name)
}
// const getNickname = (e) => {
// formData.value.name = e.detail.value
// console.log(formData.value.name)
// }
onMounted(async () => {
await getAdministrativeDivisionTree()

View File

@ -30,6 +30,7 @@ class CustomRequest {
BASE_API: string = process.env.TARO_APP_BASE_API;
private request<T>(url: string, method: keyof Taro.request.Method, options: ApiOptions, params?: object,): Promise<JsonResult<T>> {
// console.log(this.BASE_API,'0000000')
return new Promise<JsonResult<T>>((resolve, reject) => {
if (options.loading) {
Taro.showLoading({

View File

@ -62,7 +62,7 @@
<text>职位:{{ item.workPost ? item.workPost : '无' }}</text>
</view>
<view style="display: flex;justify-content: space-between">
<text>保安证件:{{ item.securityNumber ? item.securityNumber : '125241256451' }}</text>
<text>保安证件:{{ item.securityNumber ? item.securityNumber : '' }}</text>
<text>出生年月:{{ dayjs(item.dateOfBirth).format('YYYY-MM-DD') }}</text>
</view>
<view style="display: flex;justify-content: space-between">

View File

@ -0,0 +1,13 @@
export interface DataStatisticsRes {
/*企事业单位数量 */
enterprisesUnitCount: number;
/*服务项目数量 */
serviceProjectCount: number;
/*保安人员数量 */
securityUserCount: number;
/*无证保安人员数量 */
noCardSecurityUserCount: number;
}

View File

@ -16,6 +16,7 @@ declare module 'vue' {
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
ADivider: typeof import('ant-design-vue/es')['Divider']
AdministrativeDivisionTree: typeof import('./src/components/tree/AdministrativeDivisionTree.vue')['default']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem']

View File

@ -1,101 +1,107 @@
declare const __APP_ENV: ImportMetaEnv;
declare global {
/**
*
*/
interface JsonResult<T> {
code: number;
message: string;
data?: T;
}
export interface SecurityUnitPagerQueryParams {
/** 名称 **/
name?: string;
/** 社会编码 **/
socialCode?: string;
/** 行政区划编码 **/
administrativeDivisionCodes?: string[];
/** 是否启用 **/
isEnable?: number;
/** 审核状态 **/
checkStatus?: number;
}
interface BaseEnum<T> {
value: T;
label: string
}
class TreeNodeVo<T, E = Record<string, any>> {
value: T;
parentValue: T;
label: string;
orderIndex?: number;
children?: TreeNodeVo<T>[]
extData?: E;
}
declare interface Grid {
//栅格占据的列数
span?: number;
//栅格左侧的间隔格数
offset?: number;
//栅格向右移动格数
push?: number;
//栅格向左移动格数
pull?: number;
//<768px 响应式栅格数或者栅格属性对象
xs?: number;
//≥768px 响应式栅格数或者栅格属性对象
sm?: number;
//≥992px 响应式栅格数或者栅格属性对象
md?: number;
//≥1200px 响应式栅格数或者栅格属性对象
lg?: number;
//≥1920px 响应式栅格数或者栅格属性对象
xl?: number;
}
interface dataStatus {
account: string;
password: string;
remark: string;
checkStatus: {
extData: {
color: string;
};
label: string;
value: number;
};
}
class SelectNodeVo<T, E = Record<string, any>> {
value: T;
label: string;
options?: SelectNodeVo<T>[]
orderIndex?: number;
disabled?: boolean;
extData?: E
}
interface ExtData {
color?: string;
}
interface Option {
label: string;
value: string | number;
extData?: ExtData | null;
}
interface OptionsResponse {
IsEnable: Option[];
IsOrNot: Option[];
Sex: Option[];
CheckStatus: Option[];
ServiceProjectType: Option[];
DeleteFlag: Option[];
}
class TreeNodeVo<T, E = Record<string, any>> {
value: T;
parentValue: T;
label: string;
orderIndex?: number;
children?: TreeNodeVo<T>[]
extData?: E;
}
/**
*
*/
interface JsonResult<T> {
code: number;
message: string;
data?: T;
}
export interface SecurityUnitPagerQueryParams {
/** 名称 **/
name?: string;
/** 社会编码 **/
socialCode?: string;
/** 行政区划编码 **/
administrativeDivisionCodes?: string[];
/** 是否启用 **/
isEnable?: number;
/** 审核状态 **/
checkStatus?: number;
}
interface BaseEnum<T> {
value: T;
label: string
}
class TreeNodeVo<T, E = Record<string, any>> {
value: T;
parentValue: T;
label: string;
orderIndex?: number;
children?: TreeNodeVo<T>[]
extData?: E;
}
interface Grid {
//栅格占据的列数
span?: number;
//栅格左侧的间隔格数
offset?: number;
//栅格向右移动格数
push?: number;
//栅格向左移动格数
pull?: number;
//<768px 响应式栅格数或者栅格属性对象
xs?: number;
//≥768px 响应式栅格数或者栅格属性对象
sm?: number;
//≥992px 响应式栅格数或者栅格属性对象
md?: number;
//≥1200px 响应式栅格数或者栅格属性对象
lg?: number;
//≥1920px 响应式栅格数或者栅格属性对象
xl?: number;
}
interface dataStatus {
account: string;
password: string;
remark: string;
checkStatus: {
extData: {
color: string;
};
label: string;
value: number;
};
}
class SelectNodeVo<T, E = Record<string, any>> {
value: T;
label: string;
options?: SelectNodeVo<T>[]
orderIndex?: number;
disabled?: boolean;
extData?: E
}
interface ExtData {
color?: string;
}
interface Option {
label: string;
value: string | number;
extData?: ExtData | null;
}
interface OptionsResponse {
IsEnable: Option[];
IsOrNot: Option[];
Sex: Option[];
CheckStatus: Option[];
ServiceProjectType: Option[];
DeleteFlag: Option[];
}

View File

@ -67,6 +67,7 @@
:allowClear="item.componentsProps?.allowClear ?? true"
:options="item.options"
/>
<administrative-division-tree-comp v-else-if="item.type === 'administrativeDivisionTree'" style="width: 100%" v-model:value="modelValue[field]" v-bind="item.componentsProps" />
<a-range-picker
v-else-if="item.type === 'rangePicker'"
style="width: 100%"
@ -111,10 +112,14 @@
<script setup lang="ts" generic="T extends Record<string,any>">
import { FormInstance } from 'ant-design-vue'
import { ref } from 'vue'
import { defineAsyncComponent, ref } from 'vue'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
import { FormProMaxItemOptions, FormProMaxItemProps, FormProMaxProps } from '@/types/components/form/index.ts'
import { ComponentProps } from 'vue-component-type-helpers'
import AdministrativeDivisionTree from '@/components/tree/AdministrativeDivisionTree.vue'
const AdministrativeDivisionTreeComp: ComponentProps<typeof AdministrativeDivisionTree> = defineAsyncComponent(() => import('@/components/tree/AdministrativeDivisionTree.vue'))
const modelValue = defineModel<T>('value', {
default: {},
@ -147,7 +152,7 @@ const props = withDefaults(defineProps<FormProMaxProps<T>>(), {
scrollToFirstError: undefined,
validateOnRuleChange: undefined,
})
console.log(props)
const formProMaxRef = ref<FormInstance>(null!)
const getResponsive = (item: FormProMaxItemProps): Grid => {

View File

@ -0,0 +1,84 @@
<template>
<a-cascader
v-model:value="modelValue"
:placeholder="placeholder"
:change-on-select="changeOnSelect"
:options="administrativeDivisionTree"
:load-data="loadData"
:allow-clear="allowClear"
/>
</template>
<script setup lang="ts">
import api from "@/axios";
import {onMounted, ref} from "vue";
import {CascaderProps} from "ant-design-vue";
import {isEmpty} from "lodash-es";
withDefaults(defineProps<{
placeholder?: string,
changeOnSelect?: boolean
allowClear?: boolean
}>(), {
placeholder: '请选择行政区划',
changeOnSelect: true,
allowClear: true
})
const modelValue = defineModel('value', {
default: []
})
const administrativeDivisionTree = ref<TreeNodeVo<string>[]>([])
const loadData: CascaderProps['loadData'] = selectedOptions => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
administrativeDivisionByParentCode(targetOption.value as string).then(data => {
targetOption.loading = false
targetOption.children = data
administrativeDivisionTree.value = [...administrativeDivisionTree.value]
})
}
/**
* 根据父级编码查询行政区划
* @param code
*/
const administrativeDivisionByParentCode = async (code: string = '0'): Promise<TreeNodeVo<string>[]> => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode', {
parentCode: code
})
//
return resp.data.map(item => {
delete item.children
return item
});
}
onMounted(async () => {
administrativeDivisionTree.value = await administrativeDivisionByParentCode()
if (!isEmpty(modelValue.value)) {
const ps = modelValue.value.map(code => administrativeDivisionByParentCode(code))
Promise.all(ps).then(data => {
let i = 0;
const deepChildren = (treeData: TreeNodeVo<string>[]) => {
treeData.forEach(item => {
if (item.value === modelValue.value[i]) {
item.children = data[i]
i++;
deepChildren(item.children)
}
})
}
deepChildren(administrativeDivisionTree.value)
})
}
})
</script>
<style scoped lang="scss">
</style>

View File

@ -15,6 +15,7 @@ import {
} from "ant-design-vue";
import { Ref, UnwrapRef, VNode } from "vue";
import { ComponentProps } from "vue-component-type-helpers";
import AdministrativeDivisionTree from "@/components/tree/AdministrativeDivisionTree.vue";
type FormProMaxItemType =
| 'custom'
@ -32,7 +33,8 @@ type FormProMaxItemType =
| 'datePicker'
| 'rangePicker'
| 'timeRangePicker'
| 'timePicker';
| 'timePicker'
| 'administrativeDivisionTree'
interface FormProMaxItemCommonProps extends ComponentProps<typeof FormItem> {
label?: string,
@ -64,6 +66,7 @@ export type FormProMaxItemOptions<T> = {
| FormProMaxItemProps<'rangePicker', ComponentProps<typeof RangePicker>>
| FormProMaxItemProps<'timeRangePicker', ComponentProps<typeof TimeRangePicker>>
| FormProMaxItemProps<'timePicker', ComponentProps<typeof TimePicker>>
| FormProMaxItemProps<'administrativeDivisionTree', ComponentProps<typeof AdministrativeDivisionTree>>
}
export interface FormProMaxProps<T = {}> extends FormProps {

View File

@ -1,8 +1,7 @@
<template>
<div>
<!-- 企事业单位 -->
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns" :searchFormOptions="searchFormOptions"
:scroll="{ x }">
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns" :searchFormOptions="searchFormOptions" :scroll="{ x }">
<!-- <template #tableHeader>
<a-space>
<a-button type="primary" @click="addUserManagement">新增用户</a-button>
@ -16,18 +15,18 @@
</template>
<script setup lang="tsx">
import {storeTreeData, loadTreeFromCache} from '@/utils/DB.ts'
// import { storeTreeData, loadTreeFromCache } from '@/utils/DB.ts'
import api from '@/axios'
import {ref, reactive} from 'vue'
import { ref, reactive } from 'vue'
import TableProMax from '@/components/table/TableProMax.vue'
import {TableProMaxProps} from '@/types/components/table/index.ts'
import {ComponentExposed} from 'vue-component-type-helpers'
import {dictSelectNodes} from '@/config/dict.ts'
import {publicUnitPagerQueryParams, FromItem} from '@/types/views/publicUnit.ts'
import { TableProMaxProps } from '@/types/components/table/index.ts'
import { ComponentExposed } from 'vue-component-type-helpers'
import { dictSelectNodes } from '@/config/dict.ts'
import { publicUnitPagerQueryParams, FromItem } from '@/types/views/publicUnit.ts'
// import FormProMax from '@/components/form/FormProMax.vue'
import {FormProMaxItemOptions} from '@/types/components/form//index.ts'
import {FormExpose} from 'ant-design-vue/es/form/Form'
import {message} from 'ant-design-vue'
import { FormProMaxItemOptions } from '@/types/components/form//index.ts'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import { message } from 'ant-design-vue'
const formRef = ref<FormExpose>(null)
type TableProps = TableProMaxProps<publicUnitPagerQueryParams>
@ -46,7 +45,7 @@ const columns: TableProps['columns'] = [
{
dataIndex: 'provinceName',
title: '行政区划',
customRender: ({record}) => {
customRender: ({ record }) => {
return `${record?.provinceName}/${record?.cityName}/${record?.districtsName}/${record?.streetName}`
},
},
@ -72,14 +71,14 @@ const columns: TableProps['columns'] = [
{
dataIndex: 'contactPersonInfo',
title: '联系人姓名',
customRender: ({record}) => {
customRender: ({ record }) => {
return record?.contactPersonInfo?.name
},
},
{
dataIndex: 'contactPersonInfo',
title: '联系人手机号',
customRender: ({record}) => {
customRender: ({ record }) => {
return record?.contactPersonInfo?.telephone
},
},
@ -100,33 +99,34 @@ const addUserManagement = () => {
title.value = ''
}
const getTree = async () => {
//
const cachedData = await loadTreeFromCache()
if (cachedData) {
console.log('未发请求')
// 使
return cachedData
} else {
console.log('发起了请求')
// API
const res = await api.get<any>('/common/administrativeDivisionTree')
await storeTreeData(res.data)
return res.data
}
}
const loadOptions = async () => {
const treeData = await getTree()
searchFormOptions.treeSelect.options = treeData
}
loadOptions()
// const getTree = async () => {
// //
// const cachedData = await loadTreeFromCache()
// if (cachedData) {
// console.log('')
// // 使
// return cachedData
// } else {
// console.log('')
// // API
// const res = await api.get<any>('/common/administrativeDivisionTree')
// await storeTreeData(res.data)
// return res.data
// }
// }
// const loadOptions = async () => {
// const treeData = await getTree()
// searchFormOptions.treeSelect.options = treeData
// }
// loadOptions()
const searchFormOptions = reactive<TableProps['searchFormOptions']>({
name: {
type: 'input',
label: '名称',
},
treeSelect: {
type: 'cascader',
// type: 'cascader',
type: 'administrativeDivisionTree',
label: '行政区划',
},
telephone: {

View File

@ -8,7 +8,7 @@ VITE_APP_BASE_API=/api
VITE_APP_PROXY_URL=http://172.10.10.93:8765
# rsa 公钥
VITE_APP_RSA_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpu1C3JHZ+Ng/eVVCZtwKsOZv9RktpAL13pKy4FoRHyNv2t8TPV2AMzLzfEzlWx001nBxyVxEMR2N9jAcqFLHv7r16ciOzbtzB9dky2G+bc9jIs4/EdVK5bAZcPRh5Jrb78sC9PHyR4AeceDyCIKHLUbWBJB4NTZE0s1Wh5kMynQIDAQAB
VITE_APP_RSA_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJps/EXxxSpEM1Ix4R0NWIOBciHCr7P7coDT8tNKfelgR7txcJOqHCO/MIWe7T04aHQTcpQxqx9hMca7dbqz8TZpz9jvLzE/6ZonVKxHsoFnNlHMp1/CPAJ9f6D9wYicum2KltJkmQ0g//D9W2zPCYoGOmSRFcZx/KEBa4EM53jQIDAQAB
# minio
VITE_APP_MINIO_URL=http://118.253.177.137:9000
VITE_APP_MINIO_BUCKET=police-security-dev

View File

@ -97,6 +97,12 @@
:allowClear="item.componentsProps?.allowClear ?? true"
:options="item.options"
/>
<AdministrativeDivisionsTree
v-else-if="item.type ==='administrativeDivisionsTree'"
style="width: 100%"
v-model:value="modelValue[field]"
v-bind="item.componentsProps"
/>
<a-range-picker
v-else-if="item.type ==='rangePicker'"
style="width: 100%"
@ -145,6 +151,7 @@ import {ref} from "vue";
import {FormExpose} from "ant-design-vue/es/form/Form";
import {QuestionCircleOutlined} from '@ant-design/icons-vue'
import {FormProMaxItemOptions, FormProMaxItemProps, FormProMaxProps} from "@/types/components/form/index.ts";
import AdministrativeDivisionsTree from "@/components/tree/AdministrativeDivisionsTree.vue";
const modelValue = defineModel<T>('value', {

View File

@ -0,0 +1,63 @@
<template>
<a-cascader
style="width: 500px"
v-model:value="modelValue"
:options="options"
:load-data="loadData"
placeholder="请选择行政区划"
:change-on-select="changeOnSelect"
/>
</template>
<script lang="ts" setup>
import api from "@/axios";
import {onMounted, ref, watch} from 'vue';
import type {CascaderProps} from 'ant-design-vue';
// withDefaultsde
withDefaults(defineProps<{
changeOnSelect?:boolean,
}>(),{
changeOnSelect:false
})
const modelValue = defineModel('value', {
default: [ ]
})
const options = ref<CascaderProps['options']>([]);
const DivisionTree = async () =>{
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:'0'})
options.value = resp.data as TreeNodeVo<string>[]
}
const loadData: CascaderProps['loadData'] = async selectedOptions => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:targetOption.value})
targetOption.loading = false;
targetOption.children = [options.value,...resp.data]
};
const transformData = (val:any,tree:any)=>{
for(let i = 0; i< val.length - 1;i++){
tree.forEach(async(item:any)=>{
if(item.value === val[i]){
item.children = []
let data = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:val[i]})
item.children = data.data.map((items:any)=>{
return {
label:items.label,
value:items.value,
isLeaf:items.isLeaf
}
})
transformData(val,item.children)
}
})
}
}
watch(modelValue,async ()=> await transformData(modelValue.value,options.value))
onMounted(async () => {
await DivisionTree()
await transformData(modelValue.value,options.value)
});
</script>

View File

@ -6,6 +6,7 @@
v-else
:src="minioBaseUrl+modelValue"
alt="avatar"/>
<a-button class="btn-success" @click="selectFile">{{ btnLabel }}</a-button>
<input id="myFileInput" type="file" style="display: none" ref="fileInput" />
</div>

View File

@ -32,7 +32,8 @@ type FormProMaxItemType =
| 'datePicker'
| 'rangePicker'
| 'timeRangePicker'
| 'timePicker';
| 'timePicker'
| 'administrativeDivisionsTree';
interface FormProMaxItemCommonProps extends ComponentProps<typeof FormItem> {
label?: string,
@ -64,6 +65,7 @@ export type FormProMaxItemOptions<T> = {
| FormProMaxItemProps<'rangePicker', ComponentProps<typeof RangePicker>>
| FormProMaxItemProps<'timeRangePicker', ComponentProps<typeof TimeRangePicker>>
| FormProMaxItemProps<'timePicker', ComponentProps<typeof TimePicker>>
| FormProMaxItemProps<'administrativeDivisionsTree', ComponentProps<Record<string, any>>>
}
export interface FormProMaxProps<T = {}> extends FormProps {

View File

@ -28,8 +28,16 @@
<a-input :allowClear="true" v-model:value="formDate.nature"/>
</a-form-item>
<a-form-item label="行政区划" name="administrativeDivisionCodes">
<a-cascader v-model:value="formDate.administrativeDivisionCodes" :show-search="{ filter }" :options="administrativeDivisionTree" @change="searchAdministrativeDivisionTree" />
</a-form-item>
<a-cascader
v-model:value="formDate.administrativeDivisionCodes"
:options="administrativeDivisionTree"
:load-data="loadData"
:show-search="{ filter }"
@change="searchAdministrativeDivisionTree"
placeholder="请选择行政区划"
change-on-select
/>
</a-form-item>
<a-form-item label="营业执照" name="businessLicense">
<SingleImageFileUpload v-model:value="formDate.businessLicense" ref="fileUpload"></SingleImageFileUpload>
</a-form-item>
@ -73,7 +81,7 @@ import type { Rule } from 'ant-design-vue/es/form';
import type { ShowSearchType } from 'ant-design-vue/es/cascader';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import api from "@/axios";
import {message, Modal} from 'ant-design-vue';
import {CascaderProps, message, Modal} from 'ant-design-vue';
import SingleImageFileUpload from "@/components/upload/SingleImageFileUpload.vue";
import {useRouter} from "vue-router";
import {formDatePort, statusPort} from "@/types/views/enterprise.ts";
@ -119,11 +127,36 @@ const rules: Record<string, Rule[]> = {
// 1
const DivisionTree = async ()=>{
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionTree')
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:'0'})
administrativeDivisionTree.value = resp.data as TreeNodeVo<string>[]
}
const loadData: CascaderProps['loadData'] = async selectedOptions => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:targetOption.value})
targetOption.loading = false;
targetOption.children = [administrativeDivisionTree.value,...resp.data]
};
//
const transformData = (val:any,tree:any)=>{
for(let i = 0; i< val.length - 1;i++){
tree.forEach(async(item:any)=>{
if(item.value === val[i]){
item.children = []
let data = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:val[i]})
item.children = data.data.map((items:any)=>{
return {
label:items.label,
value:items.value
}
})
transformData(val,item.children)
}
})
}
}
// 2
const filter: ShowSearchType['filter'] = (inputValue, path) => {
return path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
@ -133,6 +166,10 @@ const filter: ShowSearchType['filter'] = (inputValue, path) => {
const searchAdministrativeDivisionTree = (e:Array<string>)=>{
formDate.value.administrativeDivisionCodes = e as any
}
//
const onFinish = async ()=>{
//
@ -153,7 +190,12 @@ const onFinish = async ()=>{
const resp = await api.post('/common/securityUnitRegister',securityUnitRegisterParams)
message.success(resp.message)
fileUpload.value.fileDelete()
await formDateRef.value.resetFields() //
await resetForm()
}
//
const resetForm = async ()=>{
await formDateRef.value.resetFields()
formDate.value = {
name:'',
socialCode:'',
@ -164,11 +206,7 @@ const onFinish = async ()=>{
address:'',
nature:''
}
}
//
const resetForm = ()=>{
formDateRef.value.resetFields()
fileUpload.value.fileDelete()
}
//
@ -214,6 +252,9 @@ const showConfirm = (columnsDate:dataStatus) => {
};
onMounted( async ()=>{
await DivisionTree()
await transformData(formDate.value.administrativeDivisionCodes,administrativeDivisionTree.value)
})
</script>

View File

@ -193,7 +193,7 @@ const columns: TableProps['columns'] = [
<a-button danger>删除</a-button>
</a-popconfirm>
<a-button type="primary" onClick={ async ()=>{
// console.log(record)
console.log(record,'9999999')
visible.value = true
serviceTitle.value = '编辑服务项目'
if(record.isRecruitSecurity === null ){
@ -221,6 +221,7 @@ const columns: TableProps['columns'] = [
formParams.value. securityUserTotal = record.securityUserTotal
formParams.value.enterprisesUnitId = record.enterprisesUnitName
formParams.value.administrativeDivisionCodes = record.enterprisesUnitAdministrativeDivisionCodes
}}>编辑</a-button>
</a-space>
}
@ -231,7 +232,7 @@ const x: number = columns.reduce((a, b) => a + (b.width as number), 0)
const administrativeDivisionTree = ref<TreeNodeVo<string>[]>([]) //
const getAdministrativeDivisionTree = async ()=>{
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionTree')
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:'0'})
administrativeDivisionTree.value = resp.data as TreeNodeVo<string>[]
}
@ -260,16 +261,15 @@ const formItemOptions = ref<FormProMaxItemOptions<serviceProjectSaveOrUpdatePara
options:userNameOptions,
},
administrativeDivisionCodes:{
type:'cascader',
type:'administrativeDivisionsTree',
label:'行政区划',
required: true,
options: administrativeDivisionTree,
componentsProps:{
showSearch:true,
onChange: async (values)=>{
const resp = await api.post<SelectNodeVo<string>[]>('/enterprisesUnit/queryListByAdministrativeDivisionCodes',values)
enterprisesUnitIdList.value = resp.data
}
onChange: async (values:any)=>{
const resp = await api.post<SelectNodeVo<string>[]>('/enterprisesUnit/queryListByAdministrativeDivisionCodes',values)
enterprisesUnitIdList.value = resp.data
},
}
},
enterprisesUnitId:{
@ -420,7 +420,7 @@ const closeModal = async()=>{
securityUserTotal:null,
remark:''
}
// await formRef.value.resetFields()
await formRef.value.resetFields()
enterprisesUnitId.value = ''
serviceTitle.value = '新增服务项目'
isRecruitSecurityHidden.value = false

View File

@ -1,11 +1,7 @@
<template>
<div>test</div>
<div style="margin: 100px auto;width: 500px">
</div>
</template>
<script setup lang="ts">
<script lang="ts" setup>
</script>
<style scoped lang="scss">
</style>