|
|
@@ -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>
|