Selaa lähdekoodia

更新应用配置,新增扫码报名页面及相关功能,优化地图组件和表单逻辑

lzj 12 tuntia sitten
vanhempi
commit
505abd62a3

+ 57 - 8
components/mapComponent/mapComponent.vue

@@ -3,6 +3,7 @@
 		<map
 			id="map"
 			@markertap="markertap"
+			@callouttap="callouttap"
 			:enable-overlooking="true"
 			enable-3D="true"
 			:scale="scale"
@@ -11,7 +12,18 @@
 			:latitude="latitudeAndLongitude.latitude"
 			:longitude="latitudeAndLongitude.longitude"
 			:show-location="latitudeAndLongitude.anchorPoint"
-		></map>
+		>
+			<cover-view slot="callout">
+				<cover-view
+					v-for="item in markers"
+					:key="item.id"
+					:marker-id="item.id"
+					class="custom_callout"
+				>
+					<cover-view class="custom_callout_text">{{ item.title }}</cover-view>
+				</cover-view>
+			</cover-view>
+		</map>
 	</view>
 	<!-- :show-location="true"定位点 -->
 </template>
@@ -41,25 +53,41 @@ export default {
 		return {
 			latitude: 24.531,
 			longitude: 118.1918,
-			scale: 12 //缩放级别,
+			scale: 12
 		};
 	},
 	mounted() {
 		// console.log(this.markers, '子组件地图');
 	},
 	methods: {
-		// 点击标记点时触发
-		markertap(e) {
+		getMarkerId(event) {
+			if (event && event.detail && event.detail.markerId !== undefined) {
+				return event.detail.markerId;
+			}
+			if (event && event.markerId !== undefined) {
+				return event.markerId;
+			}
+			return undefined;
+		},
+		handleMarkerTap(markerId) {
+			if (markerId === undefined) {
+				return;
+			}
 			if (this.introduceShow == false) {
-				this.$emit('subComponent', true, e.markerId);
-				// console.log(e, '标记点11');
+				this.$emit('subComponent', true, markerId);
 			} else {
-				// console.log(e, '标记点222');
 				uni.navigateTo({
-					url: `/index_fenbao/GuanLi/XiangQing?id=${e.markerId}`
+					url: `/index_fenbao/GuanLi/XiangQing?id=${markerId}`
 				});
 			}
 		},
+		// 点击标记点时触发
+		markertap(e) {
+			this.handleMarkerTap(this.getMarkerId(e));
+		},
+		callouttap(e) {
+			this.handleMarkerTap(this.getMarkerId(e));
+		},
 		// 获取位置
 		vicinityBtn() {
 			const that = this;
@@ -89,6 +117,27 @@ export default {
 </script>
 
 <style>
+.box {
+	width: 100%;
+}
+
+.custom_callout {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+
+.custom_callout_text {
+	padding: 8rpx 20rpx;
+	border-radius: 999rpx;
+	background-color: #e2a0a4;
+	color: #ffffff;
+	font-size: 24rpx;
+	line-height: 1.2;
+	white-space: nowrap;
+	text-align: center;
+}
+
 .map_box {
 	width: 625rpx;
 	height: 320rpx;

+ 6 - 0
config/api.js

@@ -423,6 +423,12 @@ export const getContentDetail = (data, callback) => post('getContentDetail', dat
 // 模型内容列表
 export const getContentList = (data, callback) => post('getContentList', data, callback,
 	'content/content');
+// 扫码报名列表
+export const getScanContentList = (data, callback) => post('getScanContentList', data, callback,
+	'content/content');
+// 需求岗位统计
+export const getVolunteerNumber = (data, callback) => post('getVolunteerNumber', data, callback,
+	'content/content/');
 // 模型主体栏目的内容列表
 export const getMainBodyColumnContentList = (data, callback) => post('getMainBodyColumnContentList', data, callback,
 	'content/content');

+ 172 - 0
index_fenbao/SaoMaBaoMing/district.vue

@@ -0,0 +1,172 @@
+<template>
+	<view class="district-page">
+		<u-navbar
+			@leftClick="goBack"
+			title="志愿者招募"
+			bgColor="rgba(255,255,255,0)"
+			:placeholder="true"
+			titleStyle="font-weight:bold;color:#000000"
+		></u-navbar>
+		<view class="district-content">
+			<view class="district-list">
+				<view
+					v-for="item in districtList"
+					:key="item.key"
+					class="district-item"
+				>
+					<view class="district-button" @click="selectDistrict(item)">
+						<text class="district-button-text">{{ item.name }}</text>
+					</view>
+					<text class="district-button-subtext">需招募岗位数:{{ item.recruitCount }}</text>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			type: '',
+			districtList: []
+		};
+	},
+	onLoad(options) {
+		this.type = options.type || '';
+		this.getVolunteerNumber();
+	},
+	methods: {
+		getTypeField() {
+			const typeFieldMap = {
+				volunteer: 'volunteer',
+				xuanjiang: 'xuanjiang',
+				publicize: 'publicize'
+			};
+			return typeFieldMap[this.type] || '';
+		},
+		getDistrictRecruitCount(item = {}) {
+			const typeField = this.getTypeField();
+			if (!typeField) {
+				return 0;
+			}
+			const count = Number(item[typeField]);
+			return Number.isNaN(count) ? 0 : count;
+		},
+		getDistrictName(item = {}) {
+			return item.name || item.title || '';
+		},
+		normalizeDistrictList(list = []) {
+			return list.map((item, index) => ({
+				...item,
+				key: item.region_id || item.id || item.title || `district_${index}`,
+				name: item.title || item.name || '',
+				recruitCount: this.getDistrictRecruitCount(item)
+			}));
+		},
+		getVolunteerNumber() {
+			this.$api.getVolunteerNumber({ main_body_id: 1 }, (res) => {
+				const responseData = res && res.data ? res.data : res || {};
+				const districtList = Array.isArray(responseData.list) ? responseData.list : [];
+				this.districtList = this.normalizeDistrictList(districtList);
+			});
+		},
+		goBack() {
+			uni.navigateBack({
+				fail() {
+					uni.switchTab({
+						url: '/pages/index/index'
+					});
+				}
+			});
+		},
+		selectDistrict(item) {
+			if (!item.recruitCount) {
+				this.$common.errorToShow('当前无岗位需求');
+				return;
+			}
+
+			const query = [`regionName=${encodeURIComponent(this.getDistrictName(item))}`];
+			if (item.region_id !== undefined && item.region_id !== null && item.region_id !== '') {
+				query.push(`region_id=${encodeURIComponent(item.region_id)}`);
+			}
+			if (this.type) {
+				query.unshift(`type=${encodeURIComponent(this.type)}`);
+			}
+
+			uni.navigateTo({
+				url: `/index_fenbao/fuWu/baoMing/baoMing2?${query.join('&')}`
+			});
+		}
+	}
+};
+</script>
+
+<style scoped>
+.district-page {
+	min-height: 100vh;
+	display: flex;
+	flex-direction: column;
+	box-sizing: border-box;
+	background-image: url('https://wwgj.wenlvti.net/uploads/20260318/2663cbc99de9048f820be7badf3fb1e6.png');
+	background-repeat: no-repeat;
+	background-position: center top;
+	background-size: 100% 100%;
+}
+
+.district-content {
+	flex: 1;
+	display: flex;
+	align-items: flex-start;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 8vh 0 60rpx;
+}
+
+.district-list {
+	width: 100%;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+}
+
+.district-item {
+	width: 80%;
+	margin-bottom: 56rpx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+}
+
+.district-button {
+	width: 100%;
+	height: 110rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-image: url('https://wwgj.wenlvti.net/uploads/20260318/ad8233a3c8306fbb2fc2ff5738f0fcb4.png');
+	background-repeat: no-repeat;
+	background-position: center;
+	background-size: 100% 100%;
+}
+
+.district-item:last-child {
+	margin-bottom: 0;
+}
+
+.district-button-text {
+	color: #ffffff;
+	font-size: 52rpx;
+	font-weight: 600;
+	line-height: 1;
+	letter-spacing: 8rpx;
+	text-indent: 8rpx;
+}
+
+.district-button-subtext {
+	margin-top: 18rpx;
+	color: #6a412c;
+	font-size: 24rpx;
+	line-height: 1.2;
+}
+</style>

+ 140 - 0
index_fenbao/SaoMaBaoMing/job.vue

@@ -0,0 +1,140 @@
+<template>
+	<view class="job-page">
+		<image
+			class="job-header-image"
+			src="https://wwgj.wenlvti.net/uploads/20260318/e94fb36598d415e39e7cd3d4bd6f915a.png"
+			mode="widthFix"
+		/>
+
+		<view class="job-buttons">
+			<view class="job-button" @click="goToPage('volunteer')">
+			<view class="job-button-content">
+				<text class="job-button-text">巡查志愿者</text>
+				<text class="job-button-subtext">需招募岗位数:{{ summary.volunteer || 0 }}</text>
+			</view>
+			</view>
+
+			<view class="job-button" @click="goToPage('xuanjiang')">
+			<view class="job-button-content">
+				<text class="job-button-text">宣讲志愿者</text>
+				<text class="job-button-subtext">需招募岗位数:{{ summary.xuanjiang || 0 }}</text>
+			</view>
+			</view>
+
+			<view class="job-button" @click="goToPage('publicize')">
+			<view class="job-button-content">
+				<text class="job-button-text">传播志愿者</text>
+				<text class="job-button-subtext">需招募岗位数:{{ summary.publicize || 0 }}</text>
+			</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			summary: {
+				title: '',
+				volunteer: 0,
+				xuanjiang: 0,
+				publicize: 0
+			}
+		};
+	},
+	onShow() {
+		this.getVolunteerNumber();
+	},
+	methods: {
+		getVolunteerNumber() {
+			this.$api.getVolunteerNumber({ main_body_id: 1 }, (res) => {
+				const responseData = res && res.data ? res.data : res || {};
+				const summary = responseData.summary || {};
+				this.summary = {
+					title: summary.title || '',
+					volunteer: Number(summary.volunteer) || 0,
+					xuanjiang: Number(summary.xuanjiang) || 0,
+					publicize: Number(summary.publicize) || 0
+				};
+			});
+		},
+		goToPage(type) {
+			const urlMap = {
+				volunteer: '/index_fenbao/SaoMaBaoMing/saoMaBaoMing',
+				xuanjiang: '/index_fenbao/SaoMaBaoMing/district',
+				publicize: '/index_fenbao/SaoMaBaoMing/district'
+			};
+
+			uni.navigateTo({
+				url: `${urlMap[type]}?type=${type}`
+			});
+		}
+	}
+};
+</script>
+
+<style scoped>
+.job-page {
+	min-height: 100vh;
+	box-sizing: border-box;
+	padding: 25% 0 0 0;
+	background-image: url('https://wwgj.wenlvti.net/uploads/20260318/2663cbc99de9048f820be7badf3fb1e6.png');
+	background-repeat: no-repeat;
+	background-position: center top;
+	background-size: 100% 100%;
+}
+
+.job-header-image {
+	display: block;
+	width: 80%;
+	margin: 0 auto;
+}
+
+.job-buttons {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	padding-top: 130rpx;
+}
+
+.job-button {
+	width: 76%;
+	aspect-ratio: 22/8;
+	margin-bottom: 80rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-image: url('https://wwgj.wenlvti.net/uploads/20260318/b85db3427f60df0b7522ee47d4bf9781.png');
+	background-repeat: no-repeat;
+	background-position: center;
+	background-size: 100% 100%;
+}
+
+.job-button:last-child {
+	margin-bottom: 0;
+}
+
+.job-button-content {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+}
+
+.job-button-text {
+	color: #ffffff;
+	font-size: 40rpx;
+	font-weight: 600;
+	letter-spacing: 16rpx;
+	text-indent: 16rpx;
+	line-height: 1;
+}
+
+.job-button-subtext {
+	margin-top: 18rpx;
+	color: #ffffff;
+	font-size: 24rpx;
+	line-height: 1.2;
+}
+</style>

+ 427 - 0
index_fenbao/SaoMaBaoMing/saoMaBaoMing.vue

@@ -0,0 +1,427 @@
+<template>
+	<view class="box">
+		<u-navbar
+			@leftClick="rightClick"
+			title="志愿者招募"
+			bgColor="rgba(255,255,255,0)"
+			:placeholder="true"
+			titleStyle="font-weight:bold;color:#000000"
+		></u-navbar>
+
+		<view class="fj_box">
+			<view class="scarch_box2">
+				<u--input
+					@change="search"
+					@input="onInputChange"
+					placeholderStyle="color: #985741"
+					height="25"
+					prefixIcon="search"
+					shape="square"
+					placeholder="输入文物关键词"
+					v-model.trim="value"
+				></u--input>
+			</view>
+		</view>
+
+		<view style="position: relative">
+			<u-transition :show="searchList.length > 0">
+				<scroll-view v-if="searchList.length > 0" class="search_box" scroll-y="true" @scrolltolower="LoadMore">
+					<view>
+						<view @click="searchItem(item)" class="item_tit" v-for="item in searchList" :key="item.id">
+							{{ item.title }}
+						</view>
+					</view>
+				</scroll-view>
+			</u-transition>
+		</view>
+
+		<u-popup :show="introduceShow" @close="close" mode="center" :closeable="true" bgColor="#f9dbbf" round="5">
+			<view class="xx_box">
+				<view style="font-size: 40rpx; font-weight: bold; text-align: center;">{{ claimDetails.title }}</view>
+				<view class="xx_tit" v-if="claimDetails.intro">
+					<u-parse :content="claimDetails.intro"></u-parse>
+				</view>
+				<view v-else>
+					<u-empty text="该文物暂无介绍" iconColor="#4a433d" textColor="#4a433d" mode="data"></u-empty>
+				</view>
+				<view style="font-size: 28rpx; color: #666666; padding-top: 10rpx;">{{ claimDetails.address }}</view>
+				<view class="claim_tit" @click="claimBtn">巡查志愿者报名</view>
+			</view>
+		</u-popup>
+
+		<view>
+			<mapComponent
+				ref="mychild"
+				@subComponent="subComponent"
+				:markers="markers"
+				:height="height"
+				:latitudeAndLongitude="latitudeAndLongitude"
+				:introduceShow="introduceShow"
+			></mapComponent>
+		</view>
+	</view>
+</template>
+
+<script>
+const ROLE_FIELD_CONFIG = [
+	{
+		key: 'patrol',
+		label: '文物巡查',
+		fields: ['patrolCount', 'patrol_count', 'wenwu_patrol_count', 'xuncha_count', 'inspection_count']
+	},
+	{
+		key: 'lecture',
+		label: '宣讲',
+		fields: ['lectureCount', 'lecture_count', 'xuanjiang_count', 'preach_count']
+	},
+	{
+		key: 'spread',
+		label: '传播',
+		fields: ['spreadCount', 'spread_count', 'communication_count', 'propagation_count']
+	}
+];
+
+// 现在接口里还没有岗位数量,这里先放一组稳定的假数据,方便联调视觉效果。
+const ENABLE_MOCK_ROLE_COUNT = true;
+const MOCK_ROLE_COUNTS = {
+	patrol: 2,
+	lecture: 1,
+	spread: 3
+};
+
+export default {
+	data() {
+		return {
+			volunteer_id: '',
+			longitude: '',
+			latitude: '',
+			page: 1,
+			searchList: [],
+			isProcessingItemClick: false,
+			preventSearchOnChange: false,
+			prevSearchVal: '',
+			value: '',
+			height: '1360',
+			latitudeAndLongitude: {
+				latitude: '24.504403',
+				longitude: '118.143033',
+				anchorPoint: true
+			},
+			markers: [],
+			claimDetails: {},
+			introduceShow: false,
+			entryOptions: {}
+		};
+	},
+	onLoad(options) {
+		// 扫码进入时,scene 会带在页面参数中,这里统一解析成普通对象。
+		this.entryOptions = this.parseEntryOptions(options);
+		this.applyEntryOptions(this.entryOptions);
+		this.details();
+		// Temporary: use getScanContentList for scan signup page until the dedicated API is ready.
+		this.getScanContentList();
+	},
+	methods: {
+		parseEntryOptions(options = {}) {
+			const parsedOptions = { ...options };
+			if (options.scene) {
+				const decodedScene = decodeURIComponent(options.scene);
+				decodedScene.split('&').forEach((segment) => {
+					if (!segment) {
+						return;
+					}
+					const [rawKey, rawValue = ''] = segment.split('=');
+					if (!rawKey) {
+						return;
+					}
+					parsedOptions[rawKey] = decodeURIComponent(rawValue);
+				});
+			}
+			return parsedOptions;
+		},
+		// 把二维码参数里的定位或关键词直接带入首屏,方便后面按点位跳转。
+		applyEntryOptions(options = {}) {
+			const keyword = options.keywords || options.keyword || options.title || '';
+			if (keyword) {
+				this.value = keyword;
+				this.prevSearchVal = keyword;
+			}
+			if (options.latitude && options.longitude) {
+				this.latitude = options.latitude;
+				this.longitude = options.longitude;
+				this.latitudeAndLongitude.latitude = options.latitude;
+				this.latitudeAndLongitude.longitude = options.longitude;
+			}
+		},
+		search() {
+			if (this.preventSearchOnChange) {
+				this.preventSearchOnChange = false;
+				return;
+			}
+
+			if (this.value) {
+				if (this.prevSearchVal !== this.value) {
+					this.prevSearchVal = this.value;
+					this.page = 1;
+					this.searchList = [];
+				}
+				// Temporary: search suggestions still use getScanContentList and will switch to the new scan API later.
+				this.$api.getScanContentList(
+					{
+						main_body_id: 1,
+						model_id: 1,
+						keywords: this.value || '',
+						page: this.page,
+						claim_status: '0',
+						pageSize: '10'
+					},
+					(res) => {
+						const list = Array.isArray(res.data) ? res.data : [];
+						this.searchList = this.page === 1 ? list : [...this.searchList, ...list];
+					}
+				);
+			} else {
+				this.searchList = [];
+				this.page = 1;
+				this.prevSearchVal = '';
+				this.getScanContentList();
+			}
+		},
+		onInputChange(value) {
+			if (!this.isProcessingItemClick) {
+				this.value = value;
+			}
+		},
+		searchItem(item) {
+			this.isProcessingItemClick = true;
+			this.page = 1;
+			this.value = item.title;
+			this.prevSearchVal = item.title;
+			this.searchList = [];
+			this.$nextTick(() => {
+				this.isProcessingItemClick = false;
+			});
+			this.latitude = item.latitude || '';
+			this.longitude = item.longitude || '';
+			this.latitudeAndLongitude.latitude = item.latitude || this.latitudeAndLongitude.latitude;
+			this.latitudeAndLongitude.longitude = item.longitude || this.latitudeAndLongitude.longitude;
+			this.getScanContentList();
+		},
+		LoadMore() {
+			if (!this.value) {
+				return;
+			}
+			this.page++;
+			this.search();
+		},
+		getScanContentList() {
+			// Temporary: use getScanContentList for scan signup page until the dedicated API is ready.
+			this.$api.getScanContentList(
+				{
+					model_id: '1',
+					main_body_id: '1',
+					page: 1,
+					pageSize: '100',
+					region: 5,
+					keywords: this.value,
+					longitude: this.longitude,
+					latitude: this.latitude
+				},
+				(res) => {
+					const list = Array.isArray(res.data) ? res.data : [];
+					if (this.value && list.length <= 0) {
+						this.$common.errorToShow('该文物已被认领');
+					}
+					this.markers = list.map((item) => this.buildMarker(item));
+				}
+			);
+		},
+		// 统一构造 marker,后续换新接口时只需要改这里的字段映射即可。
+		buildMarker(item) {
+			return {
+				id: parseFloat(item.id),
+				latitude: parseFloat(item.latitude),
+				longitude: parseFloat(item.longitude),
+				iconPath: '/static/img/icon_map.png',
+				width: 1,
+				height: 1,
+				alpha: 0,
+				title: item.title,
+				customCallout: {
+					display: 'ALWAYS'
+				},
+				joinCluster: true,
+			};
+		},
+		getRoleCount(source, fields = []) {
+			for (let index = 0; index < fields.length; index++) {
+				const field = fields[index];
+				if (source[field] !== undefined && source[field] !== null && source[field] !== '') {
+					const count = Number(source[field]);
+					if (!Number.isNaN(count) && count > 0) {
+						return count;
+					}
+				}
+			}
+			return 0;
+		},
+		// 点击 marker 后先用列表数据秒开岗位标签,再异步拉详情补全图文信息。
+		subComponent(newShow, id) {
+			this.introduceShow = newShow;
+			this.claimDetails = {};
+			this.$api.getContentDetail(
+				{
+					main_body_id: 1,
+					id: id
+				},
+				(res) => {
+					this.claimDetails = res.data || {};
+				}
+			);
+		},
+		details() {
+			this.$api.details({ main_body_id: 1 }, (res) => {
+				if (res.code == 1) {
+					this.volunteer_id = res.data.id;
+				}
+			});
+		},
+		claimBtn() {
+			if (this.claimDetails.claim_status === 1 && this.claimDetails.is_multiple_claims === 0) {
+				this.$common.errorToShow('该文物已被认领');
+			} else if (this.volunteer_id !== undefined && this.volunteer_id !== '') {
+				this.$api.claimCr(
+					{
+						main_body_id: 1,
+						type: 'volunteer',
+						volunteer_id: this.volunteer_id,
+						cr_id: this.claimDetails.id,
+						cr_code: this.claimDetails.code,
+						desc: ''
+					},
+					(res) => {
+						this.$common.errorToShow(res.msg);
+					}
+				);
+			} else {
+				uni.navigateTo({
+					url: '/index_fenbao/fuWu/baoMing/baoMing2?type=volunteer&id=' + this.claimDetails.id
+				});
+			}
+		},
+		rightClick() {
+			uni.navigateBack({
+				fail() {
+					uni.switchTab({
+						url: '/pages/index/index'
+					});
+				}
+			});
+		},
+		close() {
+			this.introduceShow = false;
+			// 这里不能立刻清空弹窗内容。
+			// u-popup 的 close 事件发生在收起动画开始时,如果马上把详情数据清掉,
+			// 用户就会看到同一个弹窗在收起过程中闪成“暂无图片/暂无介绍”的空弹窗。
+		}
+	}
+};
+</script>
+
+<style>
+::v-deep .u-swiper-indicator__wrapper__dot--active {
+	width: 5px !important;
+}
+
+.box {
+	height: 100%;
+	width: 100%;
+	padding-bottom: 50rpx;
+	background-image: url('https://huli-app.wenlvti.net/app_static/WenWuGuanJia/image/xy_bgt.png');
+	background-repeat: repeat-y;
+	background-size: cover;
+	background-size: 100% 100%;
+}
+
+.fj_box {
+	width: 90%;
+	margin: auto;
+	margin-top: 40rpx;
+	margin-bottom: 20rpx;
+}
+
+.scarch_box2 {
+	width: 674rpx;
+	height: 82rpx;
+	padding: 6rpx 0 0 30rpx;
+	margin-top: 20rpx;
+	background-image: url('/static/img/search_bg1.png');
+	background-size: 100% 100%;
+}
+
+.xx_box {
+	padding: 20rpx;
+	background-color: #e1bf9a;
+	width: 660rpx;
+	padding-top: 80rpx;
+}
+
+.xx_tit {
+	height: 360rpx;
+	font-size: 30rpx;
+	padding: 20rpx;
+	text-indent: 2em;
+	margin-top: 30rpx;
+	background-color: #f3e3d3;
+	overflow: scroll;
+}
+
+.role_box {
+	display: flex;
+	flex-wrap: wrap;
+	gap: 18rpx;
+	margin-top: 24rpx;
+}
+
+.role_item {
+	padding: 12rpx 24rpx;
+	border-radius: 999rpx;
+	background: #f7e3d0;
+	color: #6a412c;
+	font-size: 28rpx;
+	line-height: 1;
+	border: 2rpx solid rgba(202, 86, 66, 0.2);
+}
+
+.role_count {
+	color: #ca5642;
+	font-weight: 700;
+}
+
+.claim_tit {
+	/* width: 190rpx; */
+	padding: 8rpx 20rpx;
+	height: 70rpx;
+	margin: 30rpx auto 0;
+	font-weight: 600;
+	text-align: center;
+	line-height: 70rpx;
+	font-size: 32rpx;
+	background-color: #efb681;
+}
+
+.search_box {
+	width: 87%;
+	position: absolute;
+	height: 400rpx;
+	z-index: 9;
+	left: 50rpx;
+	padding: 40rpx;
+	background-color: #f7dfc0;
+}
+
+.item_tit {
+	line-height: 50rpx;
+	font-size: 30rpx;
+}
+</style>

+ 0 - 39
index_fenbao/fuWu/baoMing/baoMing.vue

@@ -1,42 +1,3 @@
-<!--
-# 对 baoMing.vue 代码的严厉批评
-## 代码结构与风格:混乱不堪的“野路子”实现
-首先,代码结构混乱到令人发指。全局变量 that 的使用(第62行和187行)是典型的新手错误,这种做法不仅破坏了Vue组件的封装性,还可能导致内存泄漏和难以追踪的bug。在现代前端开发中,这种写法早已被唾弃,而你却堂而皇之地使用,足见代码质量之低下。
-
-代码注释严重缺失,关键逻辑如文件上传( chooseFile 方法)、表单提交( submit 方法)等没有任何说明,仿佛在挑战后续维护者的耐心。变量命名更是随心所欲, cr_id 、 modifyId 、 volunteer_id 等命名风格不统一,一会儿下划线,一会儿驼峰,完全无视代码规范的存在。
-
-## 功能实现:漏洞百出的“豆腐渣工程”
-### 表单验证:形同虚设的防线
-表单验证规则( rules 对象)漏洞百出。身份证号字段( id_card )只做了必填验证,连最基本的18位长度验证都没有(第135-139行被注释掉),这意味着用户可以输入任意长度的字符串,后端将承受巨大的验证压力。更可笑的是,区域选择组件的 name 属性居然被设置为 id_card (第28行),与身份证字段冲突,这会导致表单验证时的逻辑混乱。
-
-提交表单时, validate(['id']) (第253行和320行)的调用更是让人摸不着头脑——表单中根本没有 id 字段,这种验证调用完全是无效的,相当于给系统开了个后门,让所有表单数据可以未经有效验证就提交。
-
-### 功能逻辑:支离破碎的实现
-文件上传功能( chooseFile 方法)设计极其简陋。只允许上传一个文件( count: 1 ),且没有文件类型和大小的限制,用户可以上传任意大小的文件,这可能导致服务器资源被恶意消耗。上传成功后,文件路径直接赋值给 valiFormData.fullurl (第240行),但在提交表单时却使用 file: fullurl (第269行),这种字段映射关系混乱不堪。
-
-志愿者详情查询( details 方法)调用API时只传递了 main_body_id: 1 (第292行),没有传递志愿者ID等关键参数,这会导致后端无法准确查询用户信息,返回的数据极有可能是错误的。修改资料功能( modifySubmit 方法)的实现同样草率, volunteer_id 和 cr_id 的获取逻辑(第334-335行)依赖于多个来源,一旦某个来源为空,就会导致API调用失败。
-
-### 安全与性能:触目惊心的隐患
-安全方面,代码没有对用户输入进行任何过滤和转义,特别是身份证号、手机号等敏感信息,直接传递给后端,存在SQL注入的风险。文件上传时, wx.chooseMessageFile (第221行)的使用没有限制文件类型,恶意用户可以上传可执行文件或其他危险文件。
-
-性能方面,表单输入没有使用防抖(debounce)处理,用户在输入时会频繁触发数据更新,增加了浏览器的渲染压力。API调用(如 getCategoryCascadeList 方法)使用了回调函数(第211-213行),而不是现代的 Promise 或 async/await 语法,代码可读性和可维护性极差。
-
-### 用户体验:粗制滥造的交互
-用户体验方面,代码的表现更是惨不忍睹。表单提交后没有加载状态提示(如loading动画),用户点击“报名”或“确认修改”按钮后,只能傻等,不知道操作是否正在进行。错误提示( that.$common.errorToShow )千篇一律,没有针对具体错误类型给出个性化提示,用户无法快速定位问题所在。
-
-区域选择组件( uni-data-picker )的 onchange 方法(第202-204行)只是获取了值却没有任何处理, onnodeclick 方法(第205-208行)虽然设置了 region_id ,但没有更新表单数据,这会导致用户选择区域后,表单中没有相应的记录。
-
-## 技术选型:固步自封的落后实践
-代码使用了uni-app框架,但完全没有遵循框架的最佳实践。例如,没有使用Vue 3的组合式API(Composition API),而是死守着过时的选项式API(Options API),导致代码逻辑分散,难以维护。组件的引用(如 uni-navbar 、 uni-forms 等)没有进行任何封装,直接在模板中使用,增加了代码的耦合度。
-
-样式部分( <style> 标签)使用了大量的内联样式和固定像素值,没有使用CSS变量或预处理器(如SCSS),导致样式难以统一管理和修改。 /deep/ 选择器(第386行)的使用是一种不良实践,它会破坏组件的样式封装性,导致样式冲突。
-
-## 总结:亟待重构的“代码垃圾”
-综上所述, baoMing.vue 代码是一个典型的反面教材,从代码结构到功能实现,从安全性能到用户体验,几乎每一个方面都存在严重问题。它不仅反映了开发者对前端开发规范的无知,也暴露了项目管理的混乱。
-
-这样的代码如果被部署到生产环境,极有可能导致系统崩溃、数据泄露等严重问题。因此,强烈建议对该代码进行全面重构,采用现代前端开发的最佳实践,如使用Vue 3的组合式API、完善表单验证、优化文件上传功能、提升用户体验等。只有这样,才能确保系统的稳定性、安全性和可维护性,为用户提供良好的使用体验。
--->
-
 <template>
 	<view class="box">
 		<u-navbar :autoBack="true" title="志愿报名" bgColor="rgba(255,255,255,0)" :placeholder="true" titleStyle="font-weight:bold;color:#000000"></u-navbar>

+ 101 - 56
index_fenbao/fuWu/baoMing/baoMing2.vue

@@ -13,16 +13,6 @@
 				<image class="top_img" src="https://huli-app.wenlvti.net/app_static/WenWuGuanJia/image/dt_ban2.png"></image>
 			</view>
 		</view>
-    <text v-if="false">
-      你将作为一个指令处理助手,为我处理用户口述的指令。
-      用户将输入一些操作指令,例如:为我开灯,打开风扇,关灯,开卧室灯,空调调到16度等等。
-      你需要根据用户的指令,提取以下关键信息并输出,json格式:
-      1. success: 指令是否处理成功。
-      2. object: 指令的操作对象(例如:灯、风扇、空调等)
-      3. action: 指令的操作动作(例如:打开、关闭、调到等)
-      4. parameter: 指令的操作参数(例如:温度、亮度等)
-      你需要注意,用户的指令可能会有错误或不完整,若指令不完整,请输出1指令处理不成功。
-    </text>
 		<!-- 表单 -->
 		<view class="example">
 			<uni-forms style="padding: 0 20rpx 0 20rpx" label-position="top" ref="valiForm" :rules="rules" :modelValue="valiFormData">
@@ -67,17 +57,8 @@
 				<uni-easyinput type="email" v-model="valiFormData.email" placeholder="请输入联系邮箱" />
 			</uni-forms-item>
 			
-			<!-- 报名选项 -->
-			<uni-forms-item label="报名选项" label-width="100px" required name="type">
-				<view class="checkbox-group">
-					<uni-data-checkbox v-model="valiFormData.type" :localdata="[
-						{value: 'volunteer', text: '巡查守护'},
-						{value: 'xuanjiang', text: '宣讲传播'}
-					]" multiple="true" />
-				</view>
-			</uni-forms-item>
-			<uni-forms-item label="您的选择" label-width="100px" required name="id_card">
-				<uni-data-picker :localdata="regionList" placeholder="请选择地区" popup-title="请选择所在地区" @change="onchange" @nodeclick="onnodeclick"></uni-data-picker>
+			<uni-forms-item label="您的选择" label-width="100px" required v-if="valiFormData.type === 'volunteer'">
+				<uni-data-picker v-model="region_id" :localdata="regionList" placeholder="请选择地区" popup-title="请选择所在地区" @change="onchange" @nodeclick="onnodeclick"></uni-data-picker>
 			</uni-forms-item>
 			<!-- 本人承诺 -->
 				<uni-forms-item label="本人承诺" label-width="100px" required name="commitment" >
@@ -121,7 +102,9 @@ export default {
 	data() {
 			return {
 				regionList: [],
-				region_id: '',
+				region_id: null,
+				pendingRegionId: '',
+				pendingRegionName: '',
 				cr_id: '' /* 文物id */,
 				modifyId: '' /* 修改资料的文物id */,
 				volunteer_id: '' /* 志愿者id */,
@@ -145,6 +128,11 @@ export default {
 				{ value: '民主党派', text: '民主党派' },
 				{ value: '群众', text: '群众' }
 			],
+			typeOptions: [
+				{ value: 'volunteer', text: '巡查守护' },
+				{ value: 'xuanjiang', text: '宣讲' },
+				{ value: 'publicize', text: '传播' }
+			],
 			
 			
 			/* 表单数据 */
@@ -158,7 +146,7 @@ export default {
 				intro: '',
 				political_status: '',
 				email: '',
-				type: [],
+				type: '',
 			},
 
 			/* 校验规则 */
@@ -291,23 +279,6 @@ export default {
 					validateTrigger: 'submit'
 				},
 				
-				// 报名选项校验
-				type: {
-					rules: [
-						{
-							required: true,
-							errorMessage: '请至少选择一个报名选项'
-						},
-						{
-							type: 'array',
-							minLength: 1,
-							errorMessage: '请至少选择一个报名选项'
-						}
-					],
-					label: '报名选项',
-					validateTrigger: 'submit'
-				},
-				
 				// 本人承诺校验
 				commitment: {
 					rules: [
@@ -333,6 +304,11 @@ export default {
 			this.cr_id = o.id;
 			this.volunteer_id = o.volunteer_id;
 			this.modifyId = o.modifyId;
+			this.pendingRegionId = o.region_id ? decodeURIComponent(o.region_id) : '';
+			this.pendingRegionName = o.regionName ? decodeURIComponent(o.regionName) : '';
+			if (o.type && this.typeOptions.some((item) => item.value === o.type)) {
+				this.valiFormData.type = o.type;
+			}
 			this.getCategoryCascadeList();
 
 			if (this.isLogin) {
@@ -359,10 +335,68 @@ export default {
 			this.region_id = node.value;
 			console.log(node);
 		},
+		getRegionValue(item = {}) {
+			if (item.value !== undefined && item.value !== null && item.value !== '') {
+				return item.value;
+			}
+			if (item.id !== undefined && item.id !== null && item.id !== '') {
+				return item.id;
+			}
+			return '';
+		},
+		getRegionText(item = {}) {
+			return item.text || item.title || item.label || item.name || '';
+		},
+		findRegionById(list = [], regionId = '') {
+			for (let index = 0; index < list.length; index++) {
+				const item = list[index];
+				if (String(this.getRegionValue(item)) === String(regionId)) {
+					return item;
+				}
+				if (Array.isArray(item.children) && item.children.length) {
+					const matchedChild = this.findRegionById(item.children, regionId);
+					if (matchedChild) {
+						return matchedChild;
+					}
+				}
+			}
+			return null;
+		},
+		findRegionByName(list = [], regionName = '') {
+			for (let index = 0; index < list.length; index++) {
+				const item = list[index];
+				if (this.getRegionText(item) === regionName) {
+					return item;
+				}
+				if (Array.isArray(item.children) && item.children.length) {
+					const matchedChild = this.findRegionByName(item.children, regionName);
+					if (matchedChild) {
+						return matchedChild;
+					}
+				}
+			}
+			return null;
+		},
+		applyPendingRegion() {
+			if (!Array.isArray(this.regionList) || !this.regionList.length) {
+				return;
+			}
+			let matchedRegion = null;
+			if (this.pendingRegionId !== '' && this.pendingRegionId !== null && this.pendingRegionId !== undefined) {
+				matchedRegion = this.findRegionById(this.regionList, this.pendingRegionId);
+			}
+			if (!matchedRegion && this.pendingRegionName) {
+				matchedRegion = this.findRegionByName(this.regionList, this.pendingRegionName);
+			}
+			if (matchedRegion) {
+				this.region_id = this.getRegionValue(matchedRegion);
+			}
+		},
 		// 区域
 		getCategoryCascadeList() {
 			this.$api.getCategoryCascadeList({ main_body_id: 1, pid: 5, level: 2 }, function (res) {
 				that.regionList = res.data;
+				that.applyPendingRegion();
 			});
 		},
 
@@ -410,6 +444,10 @@ export default {
 					that.$common.errorToShow('请先阅读并同意本人承诺');
 					return;
 				}
+				if (!this.valiFormData.type) {
+					that.$common.errorToShow('缺少报名类型,请从对应入口重新进入');
+					return;
+				}
         if (!err) {
 					console.log('校验成功');
 					/* 检验成功 */
@@ -419,21 +457,25 @@ export default {
             unit_name, intro, fullurl,
             email,
 					} = this.valiFormData;
+					const params = {
+						main_body_id: 1,
+						name: name,
+						mobile: mobile,
+						address: address,
+						unit_name: unit_name,
+						id_card: id_card,
+						intro: intro,
+						file: fullurl,
+						politics_status: political_status,
+						email: email,
+						type: this.valiFormData.type,
+						region_id: this.region_id,
+					};
+					if (this.cr_id) {
+						params.cr_id = this.cr_id;
+					}
 					this.$api.scanApplyVolunteer(
-						{
-							main_body_id: 1,
-							name: name,
-							mobile: mobile,
-							address: address,
-							unit_name: unit_name,
-							id_card: id_card,
-							intro: intro,
-							file: fullurl,
-							politics_status: political_status,
-							email: email,
-							type: this.valiFormData.type,
-							region_id: this.region_id,
-						},
+						params,
 						function (res) {
 							if (res.code === 1) {
 								that.$common.errorToShow(res.msg);
@@ -494,10 +536,10 @@ export default {
 	height: 250rpx;
 	margin: auto;
 }
-/deep/.uni-forms-item__error {
+::v-deep .uni-forms-item__error {
 	color: red !important;
 }
-/deep/ .uni-stat__select {
+::v-deep .uni-stat__select {
   padding: 0!important;
 }
 .is-input-border {
@@ -524,6 +566,7 @@ export default {
 	text-align: center;
 	font-weight: 700;
 	background: linear-gradient(180deg, #af7e44 0%, #934b36 100%);
+	background-clip: text;
 	-webkit-background-clip: text;
 	-webkit-text-fill-color: transparent;
 }
@@ -585,6 +628,7 @@ export default {
 	font-size: 36rpx;
 	letter-spacing: 6rpx;
 	background: linear-gradient(180deg, #af7e44 0%, #934b36 100%);
+	background-clip: text;
 	-webkit-background-clip: text;
 	-webkit-text-fill-color: transparent;
 }
@@ -609,6 +653,7 @@ export default {
 	font-size: 32rpx;
 	letter-spacing: 6rpx;
 	background: linear-gradient(180deg, #af7e44 0%, #934b36 100%);
+	background-clip: text;
 	-webkit-background-clip: text;
 	-webkit-text-fill-color: transparent;
 }

+ 1 - 1
manifest.json

@@ -1,6 +1,6 @@
 {
     "name" : "文物管家",
-    "appid" : "__UNI__630FB2B",
+    "appid" : "__UNI__45DC458",
     "description" : "文物管家",
     "versionName" : "1.0.0",
     "versionCode" : "100",

+ 31 - 1
pages.json

@@ -267,6 +267,36 @@
 			]
 		},
 		{
+			"root": "index_fenbao/SaoMaBaoMing",
+			"pages": [{
+					"path": "saoMaBaoMing",
+					"style": {
+						"navigationBarTitleText": "",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "job",
+					"style": {
+						"navigationBarTitleText": "",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path" : "district",
+					"style" : 
+					{
+						"navigationBarTitleText" : "",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				}
+
+			]
+		},
+		{
 			"root": "index_fenbao/fuWu/jiJin",
 			"pages": [{
 					"path": "jiJin",
@@ -1042,4 +1072,4 @@
 	},
 	"sitemapLocation": "sitemap.json"
 
-}
+}

+ 6 - 1
pages/user/index.vue

@@ -138,7 +138,12 @@ export default {
 					title: '我的活动',
 					img: '/static/img/icon_u5.png',
 					path: '/user_fenbao/huoDong/huoDong'
-				}
+				},
+				// {
+				// 	title: '报名',
+				// 	img: '/static/img/icon_u1.png',
+				// 	path: '/index_fenbao/SaoMaBaoMing/job'
+				// }
 			]
 		};
 	},