wzx 1 rok pred
rodič
commit
58693559da
100 zmenil súbory, kde vykonal 4153 pridanie a 0 odobranie
  1. 41 0
      card/App.vue
  2. 73 0
      card/common/api.js
  3. 29 0
      card/common/define.js
  4. 369 0
      card/common/tools.js
  5. 174 0
      card/components/my-popup/my-popup.vue
  6. 142 0
      card/components/my-ranklist/my-ranklist.vue
  7. 20 0
      card/index.html
  8. 22 0
      card/main.js
  9. 83 0
      card/manifest.json
  10. 60 0
      card/pages.json
  11. 147 0
      card/pages/bm/style1/cardconfig.md
  12. 341 0
      card/pages/bm/style1/index.vue
  13. 527 0
      card/pages/bm/style1/rankList.vue
  14. 615 0
      card/pages/bm/style1/signup.vue
  15. 16 0
      card/pages/index/index.vue
  16. 96 0
      card/pages/jbs/cardconfig.md
  17. 331 0
      card/pages/jbs/index.vue
  18. 456 0
      card/pages/jbs/rankList.vue
  19. 444 0
      card/pages/mytz/detail.vue
  20. 167 0
      card/pages/mytz/index.vue
  21. BIN
      card/static/backgroud/top_bg.png
  22. BIN
      card/static/backgroud/top_bg1.png
  23. BIN
      card/static/backgroud/top_colorbar.png
  24. BIN
      card/static/backgroud/top_run.png
  25. BIN
      card/static/cardbg/xfl.png
  26. BIN
      card/static/common/hdlc.png
  27. BIN
      card/static/common/jbbs.png
  28. BIN
      card/static/cup/1/001.png
  29. BIN
      card/static/cup/1/001h.png
  30. BIN
      card/static/cup/1/002.png
  31. BIN
      card/static/cup/1/002h.png
  32. BIN
      card/static/cup/1/003.png
  33. BIN
      card/static/cup/1/003h.png
  34. BIN
      card/static/cup/1/004.png
  35. BIN
      card/static/cup/1/004h.png
  36. BIN
      card/static/cup/1/005.png
  37. BIN
      card/static/cup/1/005h.png
  38. BIN
      card/static/cup/1/006.png
  39. BIN
      card/static/cup/1/006h.png
  40. BIN
      card/static/cup/1/007.png
  41. BIN
      card/static/cup/1/007h.png
  42. BIN
      card/static/cup/1/008.png
  43. BIN
      card/static/cup/1/008h.png
  44. BIN
      card/static/cup/1/009.png
  45. BIN
      card/static/cup/1/009h.png
  46. BIN
      card/static/cup/1/010.png
  47. BIN
      card/static/cup/1/010h.png
  48. BIN
      card/static/cup/1/011.png
  49. BIN
      card/static/cup/1/011h.png
  50. BIN
      card/static/cup/1/012.png
  51. BIN
      card/static/cup/1/012h.png
  52. BIN
      card/static/default/back.png
  53. BIN
      card/static/default/cal.png
  54. BIN
      card/static/default/clock.png
  55. BIN
      card/static/default/info.png
  56. BIN
      card/static/default/medal_copper.png
  57. BIN
      card/static/default/medal_gold.png
  58. BIN
      card/static/default/medal_other.png
  59. BIN
      card/static/default/medal_silver.png
  60. BIN
      card/static/logo.png
  61. BIN
      card/static/logo/jbs.png
  62. BIN
      card/static/logo/mytz.png
  63. BIN
      card/static/logo/poly.png
  64. BIN
      card/static/logo/sddx.png
  65. BIN
      card/static/logo/sqsj.png
  66. BIN
      card/static/logo/xfl.png
  67. BIN
      card/static/medal/100km.png
  68. BIN
      card/static/medal/10km.png
  69. BIN
      card/static/medal/110km.png
  70. BIN
      card/static/medal/120km.png
  71. BIN
      card/static/medal/130km.png
  72. BIN
      card/static/medal/140km.png
  73. BIN
      card/static/medal/150km.png
  74. BIN
      card/static/medal/15km.png
  75. BIN
      card/static/medal/160km.png
  76. BIN
      card/static/medal/170km.png
  77. BIN
      card/static/medal/180km.png
  78. BIN
      card/static/medal/190km.png
  79. BIN
      card/static/medal/20km.png
  80. BIN
      card/static/medal/25km.png
  81. BIN
      card/static/medal/30km.png
  82. BIN
      card/static/medal/35km.png
  83. BIN
      card/static/medal/40km.png
  84. BIN
      card/static/medal/45km.png
  85. BIN
      card/static/medal/50km.png
  86. BIN
      card/static/medal/55km.png
  87. BIN
      card/static/medal/5km.png
  88. BIN
      card/static/medal/60km.png
  89. BIN
      card/static/medal/65km.png
  90. BIN
      card/static/medal/70km.png
  91. BIN
      card/static/medal/75km.png
  92. BIN
      card/static/medal/80km.png
  93. BIN
      card/static/medal/85km.png
  94. BIN
      card/static/medal/90km.png
  95. BIN
      card/static/medal/95km.png
  96. BIN
      card/static/medal/copper.png
  97. BIN
      card/static/medal/egg.png
  98. BIN
      card/static/medal/finished.png
  99. BIN
      card/static/medal/gold.png
  100. BIN
      card/static/medal/silver.png

+ 41 - 0
card/App.vue

@@ -0,0 +1,41 @@
+<script>
+	export default {
+		onLaunch: function() {
+			// console.log('App Launch')
+		},
+		onShow: function() {
+			// console.log('App Show')
+		},
+		onHide: function() {
+			// console.log('App Hide')
+		}
+	}
+</script>
+
+<style>
+	/*每个页面公共css */
+	
+	.body {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+	
+	.body-radius {
+		border-radius: 50rpx;
+		overflow: hidden; /* 确保边框圆角不会溢出 */
+	}
+	
+	.uni-row {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+	}
+	
+	.uni-column {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+	}
+</style>

+ 73 - 0
card/common/api.js

@@ -0,0 +1,73 @@
+export const ossUrl = 'http://oss-card.colormaprun.com/card/';
+
+// export const apiServer = 'https://colormaprun.com/api/card/';	// 正式版
+// export const token = '';
+
+export const apiServer = 'https://t-mapi.colormaprun.com/api/card/';	// 测试版
+export const token = '1ea8fb7c1eb3259a9b1055cc68c1d435';
+// export const token = '3ea08be20abc5f8dfee4a11b32606bc3';
+// export const token = '---';
+
+// 卡片基本信息查询
+export const apiCardBaseQuery = apiServer + 'CardBaseQuery';
+
+// 卡片对应活动或赛事详情查询
+export const apiCardDetailQuery = apiServer + 'CardDetailQuery';
+
+// 排名查询
+export const apiCardRankDetailQuery = apiServer + 'CardRankDetailQuery';
+
+// 用户是否已经报名卡片对应赛事查询
+export const apiUserJoinCardQuery = apiServer + 'UserJoinCardQuery';
+
+// 线上赛报名页面信息详情
+export const apiOnlineMcSignUpDetail = apiServer + 'OnlineMcSignUpDetail';
+
+// 线上赛报名(重新分组)
+export const apiOnlineMcSignUp = apiServer + 'OnlineMcSignUp';
+
+// 玩家当前月挑战记录查询
+export const apiCurrentMonthlyChallengeQuery = apiServer + 'CurrentMonthlyChallengeQuery';
+
+// 卡片配置信息查询
+export const apiCardConfigQuery = apiServer + 'CardConfigQuery';
+
+
+// 检测request的返回值
+export function checkResCode(res) {
+	if (res.data.code == 0) {
+		return true;
+	} else if (res.statusCode == 401) { // 未登录
+		uni.showToast({
+			title: `您尚未登录`,
+			icon: 'none',
+			duration: 3000
+		});
+		window.location.href = `action://to_login/`;
+		return false;
+	} else {
+		uni.showToast({
+			title: `${res.data.message}`,
+			icon: 'none',
+			duration: 3000
+		});
+		return false;
+	}
+};
+
+// 检测token
+export function checkToken(token) {
+	const regex = /^[0-9A-Za-f]{32}$/;
+	if (regex.test(token)) {
+		return true;
+	} else { // 未登录
+		console.log('checkToken err: ', token);
+		uni.showToast({
+			title: `您尚未登录`,
+			icon: 'none',
+			duration: 3000
+		});
+		window.location.href = `action://to_login/`;
+		return false;
+	}
+};

+ 29 - 0
card/common/define.js

@@ -0,0 +1,29 @@
+export const teamName = [];
+
+teamName[0] = [];
+teamName[0][0] = '不组队';
+teamName[0][1] = '红队';
+teamName[0][2] = '黄队';
+teamName[0][3] = '蓝队';
+teamName[0][4] = '紫队';
+
+teamName[1] = [];
+teamName[1][0] = '不组队';
+teamName[1][1] = '学生队';
+teamName[1][2] = '家长队';
+
+export const defaultPopUpDataList = [
+	{
+		type: 2,
+		data: {
+			title: "活动流程",
+			img: "/static/common/hdlc.png",
+		}
+	}, {
+		type: 2,
+		data: {
+			title: "基本标识",
+			img: "/static/common/jbbs.png",
+		}
+	},
+];

+ 369 - 0
card/common/tools.js

@@ -0,0 +1,369 @@
+var tools = {
+
+	// 动态创建<style>标签,将CSS代码插入到文档中
+	loadCssCode(cssCode) {
+		this.removeCssCode();
+		
+		const styleId = "css-custom";
+		var style = window.document.createElement("style");
+		style.type = "text/css";
+		style.id = styleId;
+		if (style.styleSheet) {
+			// This is required for IE8 and below.
+			style.styleSheet.cssText = cssCode;
+		} else {
+			style.appendChild(document.createTextNode(cssCode));
+		}
+		document.getElementsByTagName("head")[0].appendChild(style);
+		// console.log("head:", document.getElementsByTagName("head")[0]);
+		// console.log("head:", document.getElementById(styleId));
+		// console.log("style:", style);
+	},
+	
+	// 删除之前动态创建的<style>标签
+	removeCssCode() {
+		const styleId = "css-custom";
+		var oldCss = document.getElementById(styleId);
+		// console.log("oldCss:", oldCss);
+		if (oldCss != null) {
+			document.getElementsByTagName("head")[0].removeChild(oldCss);
+			console.log("oldCss 已移除");
+		}
+	},
+
+	// uni-data-select 组件,根据选中的值获取对应的文本
+	getSelectedText(obj, value) {
+		const selectedOption = obj.find(option => option.value === value);
+		return selectedOption ? selectedOption.text : '';
+	},
+
+	objectToQueryString(obj) {
+		return Object.keys(obj).map(k => k + '=' + obj[k]).join('&');
+	},
+
+	// 秒数转换成 XX天XX小时
+	convertSecondsToDHM(seconds) {
+		var days = Math.floor(seconds / (3600 * 24));
+		var hours = Math.floor((seconds % (3600 * 24)) / 3600);
+		var minutes = Math.floor((seconds % (3600 * 24)) % 3600 / 60);
+		if (days > 0)
+			// return `${days.toString().padStart(2, '0')}天${hours.toString().padStart(2, '0')}小时`;
+			return `${days}天${hours.toString().padStart(2, '0')}小时`;
+		else
+			return `${hours.toString().padStart(2, '0')}小时${minutes.toString().padStart(2, '0')}分钟`;
+	},
+
+	// 秒数转换成时分秒
+	// style:  0 [01:02:03]  1 [1h:02'3"]
+	convertSecondsToHMS(seconds, style = 0) {
+		var hours = Math.floor(seconds / 3600);
+		var minutes = Math.floor((seconds % 3600) / 60);
+		var remainingSeconds = Math.floor(seconds % 60);
+		// return hours + ":" + minutes + ":" + remainingSeconds;
+		if (style == 0)
+			return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
+		else if (style == 1) {
+			if (hours > 0)
+				return `${hours}h:${minutes}′${remainingSeconds}″`;
+			else
+				return `${minutes}′${remainingSeconds}″`;
+		}
+	},
+
+	// 计算(中英文混合)字符串长度
+	calStrLen(str) {
+		var length = 0;
+		for (var i = 0; i < str.length; i++) {
+			// 将字符转换为 Unicode 编码
+			var charCode = str.charCodeAt(i);
+			if (charCode >= 0 && charCode <= 128) {
+				length++;
+			} else {
+				length += 2;
+			}
+		}
+
+		return length;
+	},
+
+	// 集合对象去重
+	unique(arr, field) {
+		var map = {};
+		var res = [];
+		for (var i = 0; i < arr.length; i++) {
+			if (!map[arr[i][field]]) {
+				map[arr[i][field]] = 1;
+				res.push(arr[i]);
+			}
+		}
+		return res;
+	},
+
+	// 正则取出html标签
+	repalceHtml(str) {
+		var dd = str.replace(/<\/?.+?>/g, "");
+		var dds = dd.replace(/ /g, ""); //dds为得到后的内容
+		return dds;
+	},
+
+	// 判断身份证号    
+	isSfz(idcard) {
+		var id =
+			/^[1-9][0-9]{5}(19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|31)|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}([0-9]|x|X)$/
+		if (idcard === '') {
+			uni.showToast({
+				title: '请输入身份证号',
+				icon: 'none'
+			})
+		} else if (!id.test(idcard)) {
+			uni.showToast({
+				title: '身份证号格式不正确!',
+				icon: 'none'
+			})
+			return false
+		} else {
+			return false
+		}
+	},
+
+	// 判断是否是手机号   
+	isPhone(val) {
+		var patrn = /^(((1[3456789][0-9]{1})|(15[0-9]{1}))+\d{8})$/
+		if (!patrn.test(val) || val === '') {
+			uni.showToast({
+				title: '手机号格式不正确',
+				icon: 'none'
+			})
+			return false
+		} else {
+			return true
+		}
+	},
+
+	// 判断邮箱
+	isEmail(email) {
+		if (email.search(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/) != -1)
+			return true;
+		else
+			return false;
+	},
+
+	//获取随机数
+	getSuiji() {
+		var Range = Max - Min;
+		var Rand = Math.random();
+		return (Min + Math.round(Rand * Range));
+	},
+
+	//计算多长时间前
+	getDateDiff(dateTimeStamp) {
+		var minute = 1000 * 60;
+		var hour = minute * 60;
+		var day = hour * 24;
+		var halfamonth = day * 15;
+		var month = day * 30;
+		var year = day * 365;
+		var now = new Date().getTime();
+		var diffValue = now - dateTimeStamp;
+		if (diffValue < 0) {
+			return;
+		}
+		var yearC = diffValue / year;
+		var monthC = diffValue / month;
+		var weekC = diffValue / (7 * day);
+		var dayC = diffValue / day;
+		var hourC = diffValue / hour;
+		var minC = diffValue / minute;
+		if (yearC >= 1) {
+			result = "" + parseInt(yearC) + "年前";
+		}
+		if (monthC >= 1) {
+			result = "" + parseInt(monthC) + "月前";
+		} else if (weekC >= 1) {
+			result = "" + parseInt(weekC) + "周前";
+		} else if (dayC >= 1) {
+			result = "" + parseInt(dayC) + "天前";
+		} else if (hourC >= 1) {
+			result = "" + parseInt(hourC) + "小时前";
+		} else if (minC >= 1) {
+			result = "" + parseInt(minC) + "分钟前";
+		} else
+			result = "刚刚";
+		return result;
+	},
+
+	// 时间戳转时间
+	timestampToTime(timestamp, i) {
+		var date = new Date(timestamp); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
+		// console.log(date, timestamp)
+		var Y = date.getFullYear() + '-';
+		var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+		var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
+		var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
+		var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
+		var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
+		if (i == 1) {
+			return Y + M + D;
+		}
+		return Y + M + D + h + m + s;
+	},
+
+	// 是否是汉字
+	isHanzi(str) {
+		let reg = /\p{Unified_Ideograph}/ug;
+		return reg.test(str);
+	},
+
+	// 是否是字母数字
+	isStringAndNumber(str) {
+		let regNumber = new RegExp(/^[0-9A-Za-z]+$/);
+		return regNumber.test(str)
+	},
+
+	// var arr3 = [30,10,111,35,1899,50,45];
+	// 集合排序  元素数字
+	listSort(list) {
+		arr3.sort(function(a, b) {
+			return a - b;
+		})
+	},
+
+	// var arr5 = [{id:10},{id:5},{id:6},{id:9},{id:2},{id:3}];
+	// 元素  对象
+	listObjectSort(arr) {
+		arr.sort(function(a, b) {
+			return a.id - b.id
+		})
+		return arr;
+	},
+
+	/*
+	 * 忽略大小写判断字符串是否相同
+	 * @param str1
+	 * @param str2
+	 * @returns {Boolean}
+	 */
+	isEqualsIgnorecase: function(str1, str2) {
+		if (str1.toUpperCase() == str2.toUpperCase()) {
+			return true;
+		} else {
+			return false;
+		}
+	},
+
+	/**
+	 * 判断是否是数字
+	 * @param value
+	 * @returns {Boolean}
+	 */
+	isNum: function(value) {
+		if (value != null && value.length > 0 && isNaN(value) == false) {
+			return true;
+		} else {
+			return false;
+		}
+	},
+
+	/**
+	 * 判断是否是中文
+	 * @param str
+	 * @returns {Boolean}
+	 */
+	isChine: function(str) {
+		var reg = /^([u4E00-u9FA5]|[uFE30-uFFA0])*$/;
+		if (reg.test(str)) {
+			return false;
+		}
+		return true;
+	},
+
+	/*验证是否为图片*/
+	tmCheckImage: function(fileName) {
+		return /(gif|jpg|jpeg|png|GIF|JPG|PNG)$/ig.test(fileName);
+	},
+
+	/*验证是否为视频*/
+	tmCheckVideo: function(fileName) {
+		return /(mp4|mp3|flv|wav)$/ig.test(fileName);
+	},
+
+	/**
+	 * 去除字符串两边的空格
+	 * @param str
+	 * @returns {number|Number}
+	 * 调用方法:var str = utils.trim("abcd")
+	 */
+	trim: function(str) {
+		String.prototype.trim = function() {
+			return str.replace(/(^\s*)|(\s*$)/g, "");
+		}
+	},
+
+	// 判断密码是否符合 至少6位,包括大小写字母、数字、特殊字符
+	isPassword(val) {
+		var reg = /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)])+$)^.{8,16}$/;
+		if (val === '') {
+			uni.showToast({
+				title: '请输入密码',
+				icon: 'none'
+			})
+		} else if (!reg.test(val)) {
+			uni.showToast({
+				title: '至少6位,包括大小写字母、数字、特殊字符',
+				icon: 'none'
+			})
+			return false
+		} else {
+			return true
+		}
+	},
+
+	// 电话匿名
+	noPassByMobile(str) {
+		if (null != str && str != undefined) {
+			var pat = /(\d{3})\d*(\d{4})/;
+			return str.replace(pat, '$1****$2');
+		} else {
+			return "";
+		}
+	},
+
+	// 获取两点间的距离
+	//进行经纬度转换为距离的计算
+	Rad(d) {
+		return d * Math.PI / 180.0; //经纬度转换成三角函数中度分表形式。
+	},
+
+	/*
+	 计算距离,参数分别为第一点的纬度,经度;第二点的纬度,经度
+	 默认单位km
+	*/
+	getMapDistance(lat1, lat2, lng1, lng2) {
+		lat1 = lat1 || 0;
+		lng1 = lng1 || 0;
+		lat2 = lat2 || 0;
+		lng2 = lng2 || 0;
+
+		var rad1 = lat1 * Math.PI / 180.0;
+		var rad2 = lat2 * Math.PI / 180.0;
+		var a = rad1 - rad2;
+		var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
+		var r = 6378137;
+		var distance = r * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(rad1) * Math.cos(rad2) *
+			Math.pow(Math.sin(b / 2), 2)));
+		// console.log(lat1, lng1, lat2, lng2);
+		// console.log(distance);
+		return Math.round(distance) / 1000;
+	},
+
+	// 预览图片
+	yulanImg(item) {
+		let arr = [item]
+		uni.previewImage({
+			urls: arr,
+		});
+	},
+
+}
+
+export default tools;

+ 174 - 0
card/components/my-popup/my-popup.vue

@@ -0,0 +1,174 @@
+<template>
+	<uni-popup ref="popup" :mask-click="false" maskBackgroundColor="rgba(0, 0, 0, 0.6)">
+		<view class="popup">
+			<swiper ref="swiper" class="swiper" :current="swiperCurrent" @change="swiperChange" :indicator-dots="true"
+				indicator-active-color="rgba(46, 133, 236, 1)" :autoplay="false" :interval="5000">
+				<swiper-item v-for="(item, index) in dataList">
+					<view class="swiper-item-view uni-column" v-if="item.type == 1">
+						<text class="swiper-item-title">{{item.data.title}}</text>
+						<image mode="aspectFit" class="swiper-item-image" :src="item.data.img"></image>
+						<view class="swiper-item-time uni-row">
+							<image mode="aspectFit" class="clock" src="/static/default/clock.png"></image>
+							<text class="acttime">{{acttime}}</text>
+						</view>
+						<view class="swiper-item-content uni-column">
+							<text
+								class="introduce-content">{{item.data.content}}</text>
+						</view>
+						<button v-if="index < dataList.length - 1" class="swiper-item-button" @click="swiperNext">继续</button>
+						<button v-else class="swiper-item-button" @click="popupClose">确定</button>
+					</view>
+					
+					<view class="swiper-item-view uni-column" v-if="item.type == 2">
+						<text class="swiper-item-title">{{item.data.title}}</text>
+						<image mode="aspectFit" style="height: 474rpx; margin-bottom: 50rpx;" :src="item.data.img"></image>
+						<button v-if="index < dataList.length - 1" class="swiper-item-button" @click="swiperNext">继续</button>
+						<button v-else class="swiper-item-button" @click="popupClose">确定</button>
+					</view>
+				</swiper-item>
+			</swiper>
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+	// import tools from '/common/tools';
+	// import {
+	// 	teamName
+	// } from '/common/define';
+
+	export default {
+		name: "my-popup",
+		props: {
+			dataList: [{}],
+			acttime: "",	// 活动时间
+			teamType: {
+				type: Number,
+				default: -1
+			}
+		},
+		data() {
+			return {			
+				swiperCurrent: 0, // swiper当前所在滑块的 index
+				// item: {}
+			};
+		},
+		methods: {
+			//当前轮播索引
+			swiperChange(e) {
+				const curIndex = e.detail.current;
+				// console.log("swiperChange", curIndex, this.swiperCurrent)
+				this.swiperCurrent = curIndex;
+			},
+			swiperNext() {
+				this.swiperCurrent++;
+			},
+			popupOpen() {
+				if (this.dataList.length == 0) {
+					console.log("[popupOpen] dataList为空,禁止弹窗");
+					return;
+				}
+				
+				this.swiperCurrent = 0;
+				this.$refs.popup.open()
+			},
+			popupClose() {
+				this.$refs.popup.close()
+			},
+			// getTeamName(teamType, teamIndex) {
+			// 	return teamName[teamType][teamIndex];
+			// },
+			// onItemClick(item) {
+			// 	this.data.item = item
+			// 	this.$emit('my-combo-list-click', this.data);
+			// }
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.acttime {
+		font-weight: 550;
+		color: #333333;
+		font-size: 30rpx;
+	}
+
+	.clock {
+		width: 30rpx;
+		height: 30rpx;
+		margin-right: 20rpx;
+	}
+
+	.introduce-content {
+		color: #333333;
+		font-size: 25rpx;
+		line-height: 36rpx;
+	}
+
+	.popup {
+		width: 90vw;
+		height: 920rpx;
+		background-color: #FEFBF6;
+		border-radius: 50rpx;
+	}
+
+	.swiper {
+		height: 100%;
+	}
+
+	// .swiper-item {
+	// 	justify-content: space-between;
+	// 	/* background-color: lightblue; */
+	// }
+
+	.swiper-item-view {
+		height: 100%;
+		justify-content: space-between;
+	}
+	
+	.swiper-item-title {
+		margin-top: 60rpx;
+		margin-bottom: 20rpx;
+		font-size: 40rpx;
+		font-weight: 550;
+	}
+
+	.swiper-item-image {
+		height: 300rpx;
+	}
+
+	.swiper-item-time {
+		height: 65rpx;
+		margin-top: 20rpx;
+		padding: 0 50rpx;
+		justify-content: space-evenly;
+		background-color: white;
+		border: 0.5px solid;
+		border-color: #e7e7e7;
+		border-radius: 40rpx;
+		box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.13);
+	}
+
+	.swiper-item-content {
+		width: 80%;
+		/* height: 100rpx; */
+		margin-top: 30rpx;
+		margin-bottom: 80rpx;
+		justify-content: start;
+	}
+
+	.swiper-item-button {
+		width: 80%;
+		height: 106rpx;
+		margin-bottom: 60rpx;
+		color: #ffffff;
+		/* font-weight: bold; */
+		line-height: 106rpx;
+		background-color: #2e85ec;
+		border-radius: 27px;
+	}
+
+	::v-deep .uni-swiper-dots-horizontal {
+		bottom: 200rpx;
+	}
+</style>

+ 142 - 0
card/components/my-ranklist/my-ranklist.vue

@@ -0,0 +1,142 @@
+<template>
+	<uni-list class="list" :border="false">
+		<uni-list-item v-for="(item,index) in rankRs" :key="index" :border="false"
+			class="list-item uni-row" :class="item.isSelf ? 'list-item-isself' : ''">
+			<template v-slot:body>
+				<text class="item-rankNum"
+					:class="getMedalClass(item.rankNum)">{{item.rankNum > 0 ? item.rankNum : '--'}}</text>
+				<view class="item-detail uni-row">
+					<text class="item-userName">{{ teamType >= 0 ? getTeamName(teamType, item.userName) : item.userName}}</text>
+					<text class="item-totalTime">{{fmtTime(item.totalTime)}}</text>
+				</view>
+			</template>
+		</uni-list-item>
+	</uni-list>
+</template>
+
+<script>
+	import tools from '/common/tools';
+	import { teamName } from '/common/define';
+	
+	export default {
+		name:"my-ranklist",
+		props: {
+			rankRs: {},
+			teamType: {
+				type: Number,
+				default: -1
+			}
+		},
+		data() {
+			return {
+				// item: {}
+			};
+		},
+		methods: {
+			getTeamName(teamType, teamIndex) {
+				return teamName[teamType][teamIndex];
+			},
+			getMedalClass(rankNum) {
+				if (rankNum == 0)
+					return 'item-rankNum-other';
+				if (rankNum <= 3)
+					return 'item-rankNum-medal-' + rankNum;
+				else if (rankNum <= 10)
+					return 'item-rankNum-medal-other';
+				else
+					return 'item-rankNum-other';
+			},
+			fmtTime(time) {
+				if (time > 0)
+					return tools.convertSecondsToHMS(time, 1);
+				else
+					return '--';
+			},
+			// onItemClick(item) {
+			// 	this.data.item = item
+			// 	this.$emit('my-combo-list-click', this.data);
+			// }
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.list {
+		width: 90%;
+		height: 53vh;
+		/* margin-top: 20rpx; */
+		/* margin-top: 1vh; */
+		overflow: scroll;
+	}
+
+	.list-item {
+		width: 100%;
+		height: 70rpx;
+		justify-content: flex-start;
+	}
+
+	.list-item-isself {
+		background-color: #ececea !important;
+		border-radius: 10rpx;
+	}
+
+	.item-rankNum {
+		width: 80rpx;
+		height: 50rpx;
+		text-align: center;
+		margin-top: 6rpx;
+		padding-right: 0.5rpx;
+		margin-right: 20rpx;
+		font-size: 26rpx;
+		font-weight: bold;
+		line-height: 50rpx;
+		background-repeat: no-repeat;
+		background-position-x: center;
+		background-position-y: top;
+		background-size: contain;
+	}
+
+	.item-detail {
+		width: 82%;
+		height: 60rpx;
+		padding-left: 10rpx;
+		padding-right: 10rpx;
+		border-bottom: #ececea 5rpx solid;
+		justify-content: space-between;
+	}
+
+	.item-rankNum-medal-1 {
+		color: #bd640a;
+		background-image: url("/static/default/medal_gold.png");
+	}
+
+	.item-rankNum-medal-2 {
+		color: #68758c;
+		background-image: url("/static/default/medal_silver.png");
+	}
+
+	.item-rankNum-medal-3 {
+		color: #9b3b11;
+		background-image: url("/static/default/medal_copper.png");
+	}
+
+	.item-rankNum-medal-other {
+		color: #9a140a;
+		background-image: url("/static/default/medal_other.png");
+	}
+
+	.item-rankNum-other {
+		color: #9a140a;
+	}
+
+	.item-userName {
+		font-size: 30rpx;
+	}
+
+	.item-totalTime {
+		font-size: 26rpx;
+		font-weight: 550;
+	}
+	
+</style>

+ 20 - 0
card/index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 22 - 0
card/main.js

@@ -0,0 +1,22 @@
+import App from './App'
+
+// #ifndef VUE3
+import Vue from 'vue'
+import './uni.promisify.adaptor'
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+  ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+export function createApp() {
+  const app = createSSRApp(App)
+  return {
+    app
+  }
+}
+// #endif

+ 83 - 0
card/manifest.json

@@ -0,0 +1,83 @@
+{
+    "name" : "card",
+    "appid" : "__UNI__A61F96B",
+    "description" : "",
+    "versionName" : "1.1.0",
+    "versionCode" : 110,
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "3",
+    "h5" : {
+        "router" : {
+            "base" : "/card/",
+            "mode" : "hash"
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : true
+            }
+        }
+    }
+}

+ 60 - 0
card/pages.json

@@ -0,0 +1,60 @@
+{
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "彩图奔跑APP卡片页面"
+			}
+		},
+		{
+			"path": "pages/mytz/index",
+			"style": {
+				"navigationBarTitleText": "每月挑战"
+			}
+		},
+		{
+			"path": "pages/mytz/detail",
+			"style": {
+				"navigationBarTitleText": "每月挑战 - 详情"
+			}
+		},
+		{
+			"path": "pages/jbs/index",
+			"style": {
+				"navigationBarTitleText": "锦标赛"
+			}
+		},
+		{
+			"path": "pages/jbs/rankList",
+			"style": {
+				"navigationBarTitleText": "锦标赛排名列表"
+			}
+		},
+		{
+			"path": "pages/bm/style1/index",
+			"style": {
+				"navigationBarTitleText": "[报名] 样式1"
+			}
+		},
+		{
+			"path": "pages/bm/style1/signup",
+			"style": {
+				"navigationBarTitleText": "[报名] 样式1 - 报名"
+			}
+		},
+		{
+			"path": "pages/bm/style1/rankList",
+			"style": {
+				"navigationBarTitleText": "[报名] 样式1 - 排名列表"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationStyle": "custom",
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "彩图奔跑",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	},
+	"uniIdRouter": {}
+}

+ 147 - 0
card/pages/bm/style1/cardconfig.md

@@ -0,0 +1,147 @@
+卡片页面定制
+
+[小飞龙]
+
+"index": {
+	"css": "
+		.content-bg{
+			background: url('static/cardbg/xfl.png');
+			background-size: cover;
+		}
+		
+		.logo{
+			width: 120px;
+			height: 120px;
+			background: url('static/logo/xfl.png') no-repeat center;
+			background-size: contain;
+		}
+	"
+},
+
+"signup": {
+	"css": "
+		.top{
+			width: 100%;
+			height: 215px;
+			padding-top: 30px;
+			padding-bottom: 30px;
+			justify-content: space-between;
+			background-image: url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+			background-repeat: no-repeat;
+			background-position: center, 0px 0px;
+			background-size: auto 176px , cover;
+		}
+		
+		.logo{
+			width: 150px;
+			height: 150px;
+			margin-top: 10px;
+			background-image: url('static/logo/xfl.png');
+			background-repeat: no-repeat;
+			background-position-x: center;
+			background-position-y: center;
+			background-size: contain;
+		}
+	",
+	"teamType": 1
+},
+
+"rankList": {
+	"css": "
+		.top{
+			width: 100%;
+			height: 26vh;
+			padding-top: 30px;
+			justify-content: space-between;
+			background-image: url('static/backgroud/top_run.png'), url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+			background-repeat: no-repeat;
+			background-position: 45px 25px, center, 0px 0px;
+			background-size: auto 27vh, auto 22vh , cover;
+		}
+	",
+	"teamType": 1
+}
+
+
+[山青世界]
+
+"index": {
+	"css": "
+		.logo{
+			width: 150px;
+			height: 120px;
+			background: url('static/logo/sqsj.png') no-repeat center;
+			background-size: contain;
+		}
+	"
+},
+
+"signup": {
+	"css": "
+		.top{
+			width: 100%;
+			height: 215px;
+			padding-top: 30px;
+			padding-bottom: 30px;
+			justify-content: space-between;
+			background-image: url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#7aedff 0%,#047200 100%);
+			background-repeat: no-repeat;
+			background-position: center, 0px 0px;
+			background-size: auto 176px , cover;
+		}
+		
+		.logo{
+			width: 150px;
+			height: 150px;
+			margin-top: 10px;
+			background-image: url('static/logo/sqsj.png');
+			background-repeat: no-repeat;
+			background-position-x: center;
+			background-position-y: center;
+			background-size: contain;
+		}
+	",
+	"popupDataList": [
+		{
+			"type": 1,
+			"data": {
+				"title": "山青活动",
+				"img": "/static/logo/sqsj.png",
+				"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务"
+			}
+		}, 
+		"default"
+	],
+	"introduce": {
+		"title" : "介绍:",
+		"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务"
+	},
+	"teamType": 0
+},
+
+"rankList": {
+	"css": "
+		.top{
+			width: 100%;
+			height: 26vh;
+			padding-top: 30px;
+			justify-content: space-between;
+			background-image: url('static/backgroud/top_run.png'), url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#7aedff 0%,#047200 100%);
+			background-repeat: no-repeat;
+			background-position: 45px 25px, center, 0px 0px;
+			background-size: auto 27vh, auto 22vh , cover;
+		}
+	",
+	"popupDataList": [
+		{
+			"type": 1,
+			"data": {
+				"title": "山青活动",
+				"img": "/static/logo/sqsj.png",
+				"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务"
+			}
+		}, 
+		"default"
+	],
+	"teamType": 0
+}

+ 341 - 0
card/pages/bm/style1/index.vue

@@ -0,0 +1,341 @@
+<!-- 
+[报名] 样式1
+http://localhost:5173/card/#/pages/bm/style1/index
+https://oss-mbh5.colormaprun.com/card/#/pages/bm/style1/index
+ -->
+<template>
+	<view class="body body-radius">
+		<view class="content" :class="cssContentBg" @click="btnClick">
+			<view class="top uni-row">
+				<view class="top-right uni-row">
+					<image mode="aspectFit" class="clock" src="/static/default/clock.png"></image>
+					<view class="countdown">{{countdown}}</view>
+				</view>
+			</view>
+			<view class="main uni-column">
+				<!-- <image mode="aspectFit" :class="cssLogo" :src="logoSrc"></image> -->
+				<view :class="cssLogo"></view>
+				<text class="type">{{type}}</text>
+				<text class="name">{{ecName}}</text>
+				<button class="button button-txtcolor">{{btnText}}</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import tools from '../../../common/tools';
+	import {
+		token,
+		ossUrl,
+		apiCardBaseQuery,
+		apiUserJoinCardQuery,
+		apiCardConfigQuery
+	} from '../../../common/api';
+	
+	export default {
+		data() {
+			return {
+				pageName: "index",
+				queryString: "",
+				token: "",
+				
+				ecId: 0, // 卡片id
+				ecName: '', // 卡片名称
+				ecDesc: '', // 卡片简介
+				beginSecond: null, // 卡片开始时间戳,单位秒
+				endSecond: null, // 卡片结束时间戳,单位秒
+				
+				isJoin: null, // 是否报名
+				
+				// mcId: 0, // 赛事id
+				// mcType: 0, // 赛事类型 1 普通活动 2 线下赛 3 线上赛
+				// mcName: "", // 赛事名称
+				
+				countdown: "", // 倒计时
+				interval: null,
+				
+				cssContentBg: "",
+				cssLogo: "",
+				// logoSrc: "",
+				type: "",
+				btnText: "",
+			}
+		},
+		computed: {},
+		onLoad(event) { // 类型非必填,可自动推导
+			// console.log(event);
+			this.queryString = tools.objectToQueryString(event);
+			// console.log(queryString);
+			this.token = event["token"] ?? token;
+			this.ecId = event["id"] ?? 0;
+			
+			// this.contentBg = "content-bg-" + (event["bg"] ?? "green");
+			// this.logoSrc = "/static/logo/" + (event["logo"] ?? "jbs") + '.png';
+			this.type = event["type"] ?? "锦标赛";
+			this.btnText = event["btnText"] ?? "开始比赛";
+			
+			tools.removeCssCode();
+			
+			this.getCardBaseQuery();
+			this.getCardConfigQuery();
+			this.getUserJoinCardQuery();
+		},
+		onUnload() {
+			this.clear();
+		},
+		methods: {
+			clear() {
+				if (this.interval != null) {
+					clearInterval(this.interval);
+					this.interval = null;
+				}
+			},
+			loadConfig(config) {
+				// console.log("config", config);
+				const css = config.css;
+				if (css != undefined && css.length > 0) {
+					tools.loadCssCode(css);
+					
+					if (css.indexOf(".content-bg{") >= 0) {
+						this.cssContentBg = "content-bg";
+					}
+					if (css.indexOf(".logo{") >= 0) {
+						this.cssLogo = "logo";
+					}
+				}
+				
+				if (this.cssContentBg == "") {
+					this.cssContentBg = "content-bg-default";
+				}
+				if (this.cssLogo == "") {
+					this.cssLogo = "logo-default";
+				}
+				console.log("[loadConfig] cssContentBg:", this.cssContentBg);
+				console.log("[loadConfig] cssLogo:", this.cssLogo);
+			},
+			// 获取倒计时
+			getCountdown() {
+				// console.log(this.endSecond)
+				if (this.endSecond > 0) {
+					const now = Date.now() / 1000;
+					const dif = this.endSecond - now;
+					// const dif = 3600*24 - 60;
+					if (dif > 0) {
+						this.countdown = tools.convertSecondsToDHM(dif);
+					} else {
+						this.countdown = "已结束";
+					}
+					// this.countdown = tools.convertSecondsToHMS(dif);
+				} else {
+					this.countdown = "--天--小时";
+				}
+			},
+			// 卡片基本信息查询
+			getCardBaseQuery() {
+				uni.request({
+					url: apiCardBaseQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId,
+						pageName: this.pageName
+					},
+					success: (res) => {
+						// console.log("getCardBaseQuery", res)
+						const data = res.data.data;
+						
+						this.ecName = data.ecName;
+						this.ecDesc = data.ecDesc;
+						this.beginSecond = data.beginSecond;
+						this.endSecond = data.endSecond;
+						
+						this.getCountdown();
+						
+						this.clear();
+						this.interval = setInterval(this.getCountdown, 60000);
+					},
+					fail: (err) => {
+						console.log("getCardBaseQuery err", err)
+					},
+				});
+			},
+			// 卡片配置信息查询
+			getCardConfigQuery() {
+				uni.request({
+					url: apiCardConfigQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId,
+						pageName: this.pageName
+					},
+					success: (res) => {
+						// console.log("getCardConfigQuery", res)
+						const data = res.data.data;
+						const config = data.configJson != "" ? JSON.parse(data.configJson) : "";
+						// console.log("configJson", config);
+						/* const config = {
+						"css": `
+							.content-bg {
+								background: url('static/cardbg/xfl.png');
+								background-size: cover;
+							}
+							
+							.logo{
+								width: 160px;
+								height: 120px;
+								background: url('static/logo/sqsj.png') no-repeat center;
+								background-size: contain;
+							}
+						`
+						}; */
+						
+						this.loadConfig(config);
+					},
+					fail: (err) => {
+						console.log("getCardConfigQuery err", err)
+					},
+				});
+			},
+			// 用户是否已经报名卡片对应赛事查询
+			getUserJoinCardQuery() {
+				uni.request({
+					url: apiUserJoinCardQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId
+					},
+					success: (res) => {
+						// console.log("getUserJoinCardQuery", res)
+						const code = res.data.code;
+						const data = res.data.data;
+						if (code == 0) {
+							this.isJoin = data.isJoin;
+						}
+					},
+					fail: (err) => {
+						console.log("getUserJoinCardQuery err", err)
+					},
+				});
+			},
+			btnClick() {
+				if (this.isJoin) {	// 已报名
+					// uni.reLaunch({
+					// 	url: '/pages/bm/sqsj/rankList?' + this.queryString
+					// });
+					window.location.href = `${ossUrl}#/pages/bm/style1/rankList?${this.queryString}`;
+				}
+				else {	// 未报名
+					// uni.reLaunch({
+					// 	url: '/pages/bm/sqsj/signup?' + this.queryString
+					// });
+					window.location.href = `${ossUrl}#/pages/bm/style1/signup?${this.queryString}`;
+				}
+				
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		width: 100vw;
+		height: 100vh;
+	}
+
+	.content-bg-default {
+		background: linear-gradient(180deg, #7aedff 0%, #047200 100%);
+		/* background: linear-gradient(180deg, #178bff 0%, #004d9b 100%); */
+		/* background: linear-gradient(180deg, #7aedff 0%, #8d2219 100%); */
+	}
+	
+	.top {
+		width: 100%;
+		justify-content: flex-end;
+	}
+
+	.top-right {
+		min-width: 180rpx;
+		height: 80rpx;
+		margin-top: 30rpx;
+		margin-right: 30rpx;
+		padding-left: 16rpx;
+		padding-right: 16rpx;
+		background-color: rgba(0, 0, 0, 0.3);
+		border-radius: 12px;
+	}
+
+	.clock {
+		width: 50rpx;
+		height: 50rpx;
+		margin-right: 12rpx;
+	}
+
+	.countdown {
+		min-width: 120rpx;
+		text-align: center;
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 46rpx;
+		/* letter-spacing: 2rpx; */
+	}
+
+	.main {
+		width: 100%;
+		height: 700rpx;
+		margin-top: 20rpx;
+		justify-content: space-evenly;
+	}
+
+	.logo-default {
+		width: 120px;
+		height: 120px;
+		background-image: url('/static/logo/jbs.png');
+		background-repeat: no-repeat;
+		background-position-x: center;
+		background-position-y: center;
+		background-size: contain;
+	}
+
+	.type {
+		opacity: 60%;
+		/* line-height: 25px; */
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 36rpx;
+		text-align: center;
+	}
+
+	.name {
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 46rpx;
+		text-align: center;
+	}
+
+	.button {
+		width: 320rpx;
+		height: 86rpx;
+		background: #ffffff;
+		border-radius: 56rpx;
+		font-size: 46rpx;
+		line-height: 80rpx;
+	}
+
+	.button-txtcolor {
+		color: #000000;
+	}
+	
+</style>
+

+ 527 - 0
card/pages/bm/style1/rankList.vue

@@ -0,0 +1,527 @@
+<!-- 
+[报名] 样式1 - 排名列表
+http://localhost:5173/card/#/pages/bm/style1/rankList
+https://oss-mbh5.colormaprun.com/card/#/pages/bm/style1/rankList
+ -->
+<template>
+	<view class="body">
+		<view class="content">
+			<view class="uni-column" :class="cssTop">
+				<view class="topbar uni-row">
+					<image mode="aspectFit" class="topbar-back" @click="btnBack" src="/static/default/back.png"></image>
+					<text class="mcName">{{mcName}}</text>
+					<image mode="aspectFit" class="topbar-info" @click="btnInfo" src="/static/default/info.png"></image>
+				</view>
+
+				<view class="topcontent uni-column">
+					<view>
+						<text class="toptext">{{coiName}}</text>
+						<text class="toptext" v-if="teamNum > 0">{{getTeamName(teamType, teamNum)}}</text>
+					</view>
+					<view class="top-countdown uni-row">
+						<image mode="aspectFit" class="cal" src="/static/default/cal.png"></image>
+						<text>{{countdown}}</text>
+					</view>
+				</view>
+
+				<view class="topbtm uni-column">
+					<view class="topbtm-content uni-row">
+						<text class="topbtm-sspm"></text>
+						<text class="topbtm-sspm">实时排名</text>
+						<view class="btnReGroup" @click="btnReGroup">重新分组</view>
+					</view>
+				</view>
+				<!-- <text class="mcName">{{ecId}} - {{mcId}} - {{token}}</text> -->
+			</view>
+			<view class="main uni-column">
+				<uni-segmented-control class="main-tab" :current="tabCurrent" :values="tabItems"
+					@clickItem="onClickTabItem" styleType="button" activeColor="#2e85ec"></uni-segmented-control>
+				<view class="tab-view uni-column">
+					<!-- 总排名 -->
+					<my-ranklist v-show="tabCurrent === 0" :rankRs="rankList.totalRankRs"></my-ranklist>
+
+					<!-- 队伍排名 -->
+					<my-ranklist v-show="tabCurrent === 1" v-if="teamType==0" :rankRs="rankList.teamRankRs" :teamType="this.teamType"></my-ranklist>
+					
+					<!-- 队内排名 -->
+					<my-ranklist v-show="tabCurrent === 2" v-if="teamType==0" :rankRs="rankList.inTeamRs"></my-ranklist>
+					
+					<!-- 学生排名 -->
+					<my-ranklist v-show="tabCurrent === 1" v-if="teamType==1" :rankRs="teamNum==1 ? rankList.inTeamRs : rankList.otherRs[1]"></my-ranklist>
+					
+					<!-- 家长排名 -->
+					<my-ranklist v-show="tabCurrent === 2" v-if="teamType==1" :rankRs="teamNum==2 ? rankList.inTeamRs : rankList.otherRs[2]"></my-ranklist>
+				</view>
+
+				<button class="btnStart" @click="btnStart">开始比赛</button>
+			</view>
+			
+			<my-popup ref="mypopup" :dataList="popupDataList" :acttime="acttime"></my-popup>
+			
+		</view>
+	</view>
+</template>
+
+<script>
+	import tools from '/common/tools';
+	import { teamName, defaultPopUpDataList } from '/common/define';
+	import {
+		token,
+		apiCardDetailQuery,
+		apiCardRankDetailQuery,
+		apiCardConfigQuery,
+		checkResCode
+	} from '/common/api';
+	
+	export default {
+		data() {
+			return {
+				pageName: "rankList",
+				// firstEnterKey: 'firstEnter-bm_sqsj_rankList',
+				firstEnterKey: 'firstEnter-bm_sqsj',
+				queryString: "",
+				token: "",
+
+				ecId: 0, // 卡片id
+				mcId: 0, // 赛事id
+				mcType: 0, // 赛事类型 1 普通活动 2 线下赛 3 线上赛
+				mcName: "", // 赛事名称
+				acttime: "", // 活动时间
+				beginSecond: null, // 活动或赛事开始时间戳,单位秒
+				endSecond: null, // 活动或赛事结束时间戳,单位秒
+				coiName: "", // 已报名单位名称,可为空
+				teamNum: 0, // 已报名队伍编号,可为0
+
+				countdown: "", // 倒计时
+				rankList: { // 排名列表
+					totalRankRs: [],
+					teamRankRs: [],
+					inTeamRs: [],
+					otherRs: []
+				},
+				interval: null,
+				
+				teamType: 0, // 队伍类型
+				dispArrStr: "", // 要显示的集合范围 (total,team,in,other)
+				tabItems: [],
+				tabCurrent: 0,
+				
+				cssTop: "",
+				popupDataList: [],
+			}
+		},
+		computed: {},
+		onLoad(event) { // 类型非必填,可自动推导
+			// console.log(event);
+			this.queryString = tools.objectToQueryString(event);
+			// console.log(queryString);
+			this.token = event["token"] ?? token;
+			this.ecId = event["id"] ?? 0;
+
+			tools.removeCssCode();
+			
+			this.getCardConfigQuery();
+		},
+		// 页面初次渲染完成,此时组件已挂载完成,DOM 树($el)已可用
+		onReady() {
+			this.dealFirstEnter();
+		},
+		onUnload() {
+			this.clear();
+		},
+		methods: {
+			dealFirstEnter() {
+				// console.log('[dealFirstEnter]');
+				let that = this;
+				uni.getStorage({
+					key: that.firstEnterKey,
+					success: (res) => {
+						console.log('[getStorage]', that.firstEnterKey, res.data);
+					},
+					fail: (e) => {
+						console.log('[getStorage] fail', that.firstEnterKey, e);
+						that.btnInfo();
+						that.setFirstEnterValue(true);
+					},
+				})
+			},
+			setFirstEnterValue(data) {
+				let that = this;
+				uni.setStorage({
+					key: that.firstEnterKey,
+					data: data,
+					success: () => {
+						console.log('[setStorage] success', that.firstEnterKey, data);
+					},
+					fail: (e) => {
+						console.log('[setStorage] fail', that.firstEnterKey, e);
+					},
+				})
+			},
+			clear() {
+				if (this.interval != null) {
+					clearInterval(this.interval);
+					this.interval = null;
+				}
+			},
+			loadConfig(config) {
+				// console.log("config", config);
+				
+				// 加载CSS样式
+				const css = config.css;
+				if (css != undefined && css.length > 0) {
+					tools.loadCssCode(css);
+					
+					if (css.indexOf(".top{") >= 0) {
+						this.cssTop = "top";
+					}
+				}
+				
+				if (this.cssTop == "") {
+					this.cssTop = "top-default";
+				}
+				console.log("[loadConfig] cssTop:", this.cssTop);
+				
+				// 加载队伍类型  0: 红黄蓝紫 1: 学生/家长
+				if (config.teamType != undefined && config.teamType >= 0) {
+					this.teamType = config.teamType;
+				}
+				if (this.teamType == 0) {
+					this.dispArrStr = "total,team,in", // 要显示的集合范围 (total,team,in,other)
+					this.tabItems = ['总排名', '队伍排名', '队内排名'];
+				} else if (this.teamType == 1) {
+					this.dispArrStr = "total,in,other", // 要显示的集合范围 (total,team,in,other)
+					this.tabItems = ['总排名', '学生排名', '家长排名'];
+				}
+				
+				// 加载弹窗数据
+				const popupDataList = config.popupDataList;
+				// console.log("[loadConfig] popupDataList:", popupDataList);
+				if (popupDataList != undefined && popupDataList.length > 0) {
+					for (var i = 0; i < popupDataList.length; i++) {
+						// console.log("[loadConfig] popupDataList", i, popupDataList[i]);
+						if (popupDataList[i] == 'default') {
+							for (var j = 0; j < defaultPopUpDataList.length; j++) {
+								this.popupDataList.push(defaultPopUpDataList[j]);
+							}
+						} else {
+							this.popupDataList.push(popupDataList[i]);
+						}
+					}
+				} else {
+					this.popupDataList = defaultPopUpDataList;
+					console.log("[loadConfig] popupDataList 加载默认列表");
+				}
+				// console.log("[loadConfig] popupDataList:", this.popupDataList);
+			},
+			// 获取倒计时
+			getCountdown() {
+				// console.log(this.endSecond)
+				if (this.endSecond > 0) {
+					const now = Date.now() / 1000;
+					const dif = this.endSecond - now;
+					// const dif = 3600*24 - 60;
+					if (dif > 0) {
+						this.countdown = '距结束 ' + tools.convertSecondsToDHM(dif);
+					} else {
+						this.countdown = "活动已结束";
+					}
+					// this.countdown = tools.convertSecondsToHMS(dif);
+				} else {
+					this.countdown = "距结束 --天--小时";
+				}
+			},
+			fmtMcTime(timestamp) {
+				var date = new Date(timestamp * 1000); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
+				// var Y = date.getFullYear() + '-';
+				var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+				var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
+				var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
+				var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes());
+				// var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
+
+				const timeStr = M + D + h + m;
+				// console.log("timeStr", timeStr);
+				return timeStr;
+			},
+			// 获取活动时间
+			getActtime() {
+				this.acttime = this.fmtMcTime(this.beginSecond) + " 至 " + this.fmtMcTime(this.endSecond);
+			},
+			getTeamName(teamType, teamIndex) {
+				return teamName[teamType][teamIndex];
+			},
+			getCardConfigQuery() {
+				uni.request({
+					url: apiCardConfigQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId,
+						pageName: this.pageName
+					},
+					success: (res) => {
+						console.log("getCardConfigQuery", res)
+						const data = res.data.data;
+						// console.log("configJson", data.configJson);
+						const config = data.configJson != "" ? JSON.parse(data.configJson) : "";
+						// console.log("configJson", data.configJson);
+						/* const config = {
+						"css": `
+							.top{
+								width: 100%;
+								height: 26vh;
+								padding-top: 30px;
+								justify-content: space-between;
+								background-image: url('static/backgroud/top_run.png'), url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+								background-repeat: no-repeat;
+								background-position: 45px 25px, center, 0px 0px;
+								background-size: auto 27vh, auto 22vh , cover;
+							}
+						`,
+						"popupDataList": [
+							{
+								"type": 1,
+								"data": {
+									"title": "山青活动",
+									"img": "/static/logo/sqsj.png",
+									"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务",
+								}
+							}, 
+							"default"
+						],
+						"teamType": 1
+						}; */
+						
+						this.loadConfig(config);
+						this.getCardDetailQuery();
+					},
+					fail: (err) => {
+						console.log("getCardConfigQuery err", err)
+					},
+				});
+			},
+			// 卡片对应活动或赛事详情查询
+			getCardDetailQuery() {
+				uni.request({
+					url: apiCardDetailQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId
+					},
+					success: (res) => {
+						// console.log("getCardDetailQuery", res)
+						const data = res.data.data;
+						this.mcType = data.mcType;
+						this.mcId = data.mcId;
+						this.mcName = data.mcName;
+						this.beginSecond = data.beginSecond;
+						this.endSecond = data.endSecond;
+						this.coiName = data.coiName;
+						this.teamNum = data.teamNum;
+
+						this.getCountdown();
+						this.getActtime();
+						this.getCardRankDetailQuery();
+
+						this.clear();
+						this.interval = setInterval(this.getCountdown, 60000);
+					},
+					fail: (err) => {
+						console.log("getCardDetailQuery err", err)
+					},
+				});
+			},
+			// 排名查询
+			getCardRankDetailQuery() {
+				uni.request({
+					url: apiCardRankDetailQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						mcIdListStr: this.mcId,
+						mcType: this.mcType,
+						dispArrStr: this.dispArrStr
+					},
+					success: (res) => {
+						console.log("getCardRankDetailQuery", res)
+						const rankdata = res.data.data;
+						this.rankList.totalRankRs = rankdata.totalRankRs;
+						this.rankList.teamRankRs = rankdata.teamRankRs;
+						this.rankList.inTeamRs = rankdata.inTeamRs;
+						this.rankList.otherRs = rankdata.otherRs;
+					},
+					fail: (err) => {
+						console.log("getCardRankDetailQuery err", err)
+					},
+				});
+			},
+			btnBack() {
+				// window.history.back();
+				window.location.href = `action://to_home/`;
+			},
+			btnReGroup() {
+				uni.navigateTo({
+					url: '/pages/bm/style1/signup?from=rankList&' + this.queryString
+				});
+			},
+			btnStart() {
+				window.location.href = `action://to_detail/?id=${this.mcId}&matchType=${this.mcType}`;
+			},
+			btnInfo() {
+				// console.log(this.$refs.mypopup);
+				this.$refs.mypopup.popupOpen();
+			},
+			onClickTabItem(e) {
+				if (this.tabCurrent != e.currentIndex) {
+					this.tabCurrent = e.currentIndex;
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		width: 100vw;
+		height: 100vh;
+	}
+
+	.top-default {
+		width: 100%;
+		height: 26vh;
+		padding-top: 30px;
+		justify-content: space-between;
+		background-image: url("/static/backgroud/top_run.png"), url("/static/backgroud/top_colorbar.png"), linear-gradient(180deg,#f8a95a 0%,#d25f11 100%);
+		/* background-image: url("/static/backgroud/top_run.png"), url("/static/backgroud/top_colorbar.png"), linear-gradient(180deg,#7aedff 0%,#047200 100%); */
+		/* background-image: url("/static/backgroud/top_run.png"), url("/static/backgroud/top_colorbar.png"), linear-gradient(180deg,#178bff 0%,#004d9b 100%); */
+		background-repeat: no-repeat;
+		background-position: 45px 25px, center, 0px 0px;
+		/* background-position-x: center; */
+		/* background-position-y: center; */
+		background-size: auto 27vh, auto 22vh , cover;
+	}
+	
+	.topbar {
+		width: 90%;
+		justify-content: space-between;
+	}
+
+	.topbar-back {
+		width: 43rpx;
+		height: 43rpx;
+		/* opacity: 0; */
+	}
+
+	.topbar-info {
+		width: 46rpx;
+		height: 46rpx;
+	}
+
+	.mcName {
+		color: white;
+		font-size: 39rpx;
+		font-weight: bold;
+	}
+
+	.topcontent {
+		/* height: 1300rpx; */
+		/* background-color: #2e85ec; */
+		margin-top: 50rpx;
+		justify-content: center;
+	}
+
+	.top-countdown {
+		height: 90rpx;
+		color: #ffdc51;
+		font-size: 28rpx;
+	}
+	
+	.cal {
+		width: 46rpx;
+		height: 46rpx;
+		margin-right: 20rpx;
+	}
+	
+	.toptext {
+		margin: 0 10rpx;
+		color: #ffffff;
+		font-size: 35rpx;
+		font-weight: 500;
+		line-height: 60rpx;
+	}
+
+	.btnReGroup {
+		width: 130rpx;
+		height: 36rpx;
+		margin-top: 15rpx;
+		margin-bottom: 15rpx;
+		background: #ffcb00;
+		border-radius: 15px;
+		color: #000000;
+		font-size: 24rpx;
+		text-align: center;
+		line-height: 36rpx;
+	}
+
+	.topbtm {
+		width: 100%;
+		height: 60rpx;
+		margin-bottom: 10rpx;
+		justify-content: space-evenly;
+	}
+
+	.topbtm-content {
+		width: 90%;
+		justify-content: space-between;
+	}
+
+	.topbtm-sspm {
+		width: 120rpx;
+		color: #ffee00;
+		font-size: 24rpx;
+		text-align: center;
+	}
+
+	.main {
+		width: 100%;
+		height: 70vh;
+		justify-content: space-around;
+		/* justify-content: space-between; */
+	}
+
+	.main-tab {
+		width: 90%;
+		margin-top: 20rpx;
+	}
+
+	.tab-view {
+		width: 100%;
+		/* height: 69vh; */
+	}
+
+	.btnStart {
+		width: 70%;
+		/* height: 6vh; */
+		height: 80rpx;
+		/* margin-bottom: 1vh; */
+		margin-bottom: 20rpx;
+		/* font-weight: bold; */
+		color: white;
+		font-size: 32rpx;
+		line-height: 80rpx;
+		background-color: #ffb40b;
+		border-radius: 27px;
+	}
+
+</style>

+ 615 - 0
card/pages/bm/style1/signup.vue

@@ -0,0 +1,615 @@
+<!-- 
+[报名] 样式1 - 报名
+http://localhost:5173/card/#/pages/bm/style1/signup
+https://oss-mbh5.colormaprun.com/card/#/pages/bm/style1/signup
+ -->
+<template>
+	<view class="body">
+		<view class="content uni-column">
+			<view class="uni-column" :class="cssTop">
+				<view class="topbar uni-row">
+					<image mode="aspectFit" class="topbar-back" @click="btnBack" src="/static/default/back.png"></image>
+					<text class="mcName">{{mcName}}</text>
+					<image mode="aspectFit" class="topbar-info" @click="btnInfo" src="/static/default/info.png"></image>
+				</view>
+				<view :class="cssLogo"></view>
+				<view class="toptime uni-row">
+					<image mode="aspectFit" class="clock" src="/static/default/clock.png"></image>
+					<text class="acttime">{{acttime}}</text>
+				</view>
+				<!-- <text class="mcName">{{ecId}} - {{mcId}} - {{token}}</text> -->
+			</view>
+			<view class="main uni-column">
+				<uni-data-select class="select" placeholder="请选择组织名称" v-model="coiId" :localdata="orgList"
+					@change="orgChange"></uni-data-select>
+				<uni-data-select v-if="teamList.length > 0" class="select" placeholder="请选择分组" v-model="teamNum"
+					:localdata="teamList"></uni-data-select>
+				<view class="introduce uni-column">
+					<text class="introduce-title">{{introduce.title}}</text>
+					<text
+						class="introduce-content">{{introduce.content}}</text>
+				</view>
+				<button class="btnSignup" @click="btnSignup">报 名</button>
+			</view>
+			
+			<my-popup ref="mypopup" :dataList="popupDataList" :acttime="acttime"></my-popup>
+			
+			<uni-popup ref="alertDialog" type="dialog">
+				<uni-popup-dialog type="info" cancelText="取消" confirmText="确认" title="您选择的是:" @confirm="dialogConfirm"
+					@close="dialogClose">
+					<view class="dialog-content uni-column">
+						<text class="dialog-content-1">{{mcName}}</text>
+						<text class="dialog-content-2">{{coiName}}</text>
+						<text class="dialog-content-2">{{teamName}}</text>
+					</view>
+				</uni-popup-dialog>
+			</uni-popup>
+		</view>
+	</view>
+</template>
+
+<script>
+	import tools from '../../../common/tools';
+	import { teamName, defaultPopUpDataList } from '../../../common/define';
+	import {
+		token,
+		apiCardDetailQuery,
+		apiOnlineMcSignUpDetail,
+		apiOnlineMcSignUp,
+		apiCardConfigQuery,
+		checkResCode,
+		checkToken
+	} from '../../../common/api';
+
+	export default {
+		data() {
+			return {
+				pageName: "signup",
+				// firstEnterKey: 'firstEnter-bm_sqsj_signup',
+				firstEnterKey: 'firstEnter-bm_sqsj',
+				queryString: "",
+				from: "", // 来源页面
+				token: "",
+				ecId: 0, // 卡片id
+				mcId: 0, // 赛事id
+				mcType: 0, // 赛事类型 1 普通活动 2 线下赛 3 线上赛
+				mcName: "", // 赛事名称
+				acttime: "", // 活动时间
+				beginSecond: null, // 活动或赛事开始时间戳,单位秒
+				endSecond: null, // 活动或赛事结束时间戳,单位秒
+				coiId: 0, // 已报名单位id
+				coiName: "", // 已报名单位名称,可为空
+				teamNum: 0, // 已报名队伍编号,可为0
+				coiRs: [], // 组织信息集合
+				orgList: [], // 分组下拉列表数据源
+				teamList: [], // 
+				interval: null,
+				
+				teamType: 0, // 队伍类型  0: 红黄蓝紫 1: 学生/家长
+				cssTop: "",
+				cssLogo: "",
+				introduce: {
+					title: "",
+					content: ""
+				},
+				popupDataList: [],
+			}
+		},
+		computed: {
+		},
+		onLoad(event) { // 类型非必填,可自动推导
+			// console.log(event);
+			this.queryString = tools.objectToQueryString(event);
+			// console.log(queryString);
+			this.from = event["from"] ?? "";
+			this.token = event["token"] ?? token;
+			this.ecId = event["id"] ?? 0;
+			
+			tools.removeCssCode();
+			
+			this.getCardConfigQuery();
+			this.getCardDetailQuery();
+		},
+		// 页面初次渲染完成,此时组件已挂载完成,DOM 树($el)已可用
+		onReady() {
+			this.dealFirstEnter();
+		},
+		onUnload() {
+			this.clear();
+		},
+		methods: {
+			dealFirstEnter() {
+				// console.log('[dealFirstEnter]');
+				let that = this;
+				uni.getStorage({
+					key: that.firstEnterKey,
+					success: (res) => {
+						console.log('[getStorage]', that.firstEnterKey, res.data);
+					},
+					fail: (e) => {
+						console.log('[getStorage] fail', that.firstEnterKey, e);
+						that.btnInfo();
+						that.setFirstEnterValue(true);
+					},
+				})
+			},
+			setFirstEnterValue(data) {
+				let that = this;
+				uni.setStorage({
+					key: that.firstEnterKey,
+					data: data,
+					success: () => {
+						console.log('[setStorage] success', that.firstEnterKey, data);
+					},
+					fail: (e) => {
+						console.log('[setStorage] fail', that.firstEnterKey, e);
+					},
+				})
+			},
+			clear() {
+				if (this.interval != null) {
+					clearInterval(this.interval);
+					this.interval = null;
+				}
+			},
+			loadConfig(config) {
+				// console.log("config", config);
+				
+				// 加载CSS样式
+				const css = config.css;
+				if (css != undefined && css.length > 0) {
+					tools.loadCssCode(css);
+					
+					if (css.indexOf(".top{") >= 0) {
+						this.cssTop = "top";
+					}
+					if (css.indexOf(".logo{") >= 0) {
+						this.cssLogo = "logo";
+					}
+				}
+				
+				if (this.cssTop == "") {
+					this.cssTop = "top-default";
+				}
+				if (this.cssLogo == "") {
+					this.cssLogo = "logo-default";
+				}
+				console.log("[loadConfig] cssTop:", this.cssTop);
+				console.log("[loadConfig] cssLogo:", this.cssLogo);
+				
+				// 加载队伍类型  0: 红黄蓝紫 1: 学生/家长
+				if (config.teamType != undefined && config.teamType >= 0) {
+					this.teamType = config.teamType;
+				}
+				
+				// 加载介绍内容
+				const introduce = config.introduce;
+				if (introduce != undefined) {
+					if (introduce.title != undefined) {
+						this.introduce.title = introduce.title;
+					}
+					if (introduce.content != undefined) {
+						this.introduce.content = introduce.content;
+					}
+				}
+				
+				// 加载弹窗数据
+				const popupDataList = config.popupDataList;
+				// console.log("[loadConfig] popupDataList:", popupDataList);
+				if (popupDataList != undefined && popupDataList.length > 0) {
+					for (var i = 0; i < popupDataList.length; i++) {
+						// console.log("[loadConfig] popupDataList", i, popupDataList[i]);
+						if (popupDataList[i] == 'default') {
+							for (var j = 0; j < defaultPopUpDataList.length; j++) {
+								this.popupDataList.push(defaultPopUpDataList[j]);
+							}
+						} else {
+							this.popupDataList.push(popupDataList[i]);
+						}
+					}
+				} else {
+					this.popupDataList = defaultPopUpDataList;
+					console.log("[loadConfig] popupDataList 加载默认列表");
+				}
+				// console.log("[loadConfig] popupDataList:", this.popupDataList);
+			},
+			getTeamList(teamNum) {
+				var teamList = [];
+				if (teamNum > 0) {
+					for (let i=0; i<=teamNum; i++) {
+						teamList[i] = {};
+						teamList[i].value = i;
+						// teamList[i].text = `第 ${i+1} 队`;
+						teamList[i].text = teamName[this.teamType][i];
+					}
+				}
+				this.teamList = teamList;
+			},
+			fmtMcTime(timestamp) {
+				var date = new Date(timestamp * 1000); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
+				// var Y = date.getFullYear() + '-';
+				var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+				var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
+				var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
+				var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes());
+				// var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
+
+				const timeStr = M + D + h + m;
+				// console.log("timeStr", timeStr);
+				return timeStr;
+			},
+			// 获取倒计时
+			getActtime() {
+				this.acttime = this.fmtMcTime(this.beginSecond) + " 至 " + this.fmtMcTime(this.endSecond);
+			},
+			getCardConfigQuery() {
+				uni.request({
+					url: apiCardConfigQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId,
+						pageName: this.pageName
+					},
+					success: (res) => {
+						console.log("getCardConfigQuery", res)
+						const data = res.data.data;
+						const config = data.configJson != "" ? JSON.parse(data.configJson) : "";
+						// console.log("configJson", data.configJson);
+						/* const config = {
+						"css": `
+							.top{
+								width: 100%;
+								height: 215px;
+								padding-top: 30px;
+								padding-bottom: 30px;
+								justify-content: space-between;
+								background-image: url("static/backgroud/top_colorbar.png"), linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+								background-repeat: no-repeat;
+								background-position: center, 0px 0px;
+								background-size: auto 176px , cover;
+							}
+							
+							.logo{
+								width: 150px;
+								height: 150px;
+								margin-top: 10px;
+								background-image: url('static/logo/xfl.png');
+								background-repeat: no-repeat;
+								background-position-x: center;
+								background-position-y: center;
+								background-size: contain;
+							}
+						`,
+						"popupDataList": [
+							{
+								"type": 1,
+								"data": {
+									"title": "山青活动",
+									"img": "/static/logo/sqsj.png",
+									"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务",
+								}
+							}, 
+							"default"
+						],
+						"introduce": {
+							"title" : "介绍:",
+							"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务",
+						},
+						"teamType": 1
+						}; */
+						
+						this.loadConfig(config);
+					},
+					fail: (err) => {
+						console.log("getCardConfigQuery err", err)
+					},
+				});
+			},
+			// 卡片信息查询
+			getCardDetailQuery() {
+				uni.request({
+					url: apiCardDetailQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId
+					},
+					success: (res) => {
+						console.log("getCardDetailQuery", res);
+						const data = res.data.data;
+						this.mcType = data.mcType;
+						this.mcId = data.mcId;
+						this.mcName = data.mcName;
+						this.beginSecond = data.beginSecond;
+						this.endSecond = data.endSecond;
+						this.coiId = data.coiId;
+						this.coiName = data.coiName;
+						this.teamNum = data.teamNum;
+
+						this.getActtime();
+						this.getOnlineMcSignUpDetail();
+
+						// this.clear();
+						// this.interval = setInterval(this.getActtime, 60000);
+					},
+					fail: (err) => {
+						console.log("getCardDetailQuery err", err)
+					},
+				});
+			},			
+			// 线上赛报名页面信息详情
+			getOnlineMcSignUpDetail() {
+				uni.request({
+					url: apiOnlineMcSignUpDetail,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						mcId: this.mcId,
+					},
+					success: (res) => {
+						console.log("getOnlineMcSignUpDetail", res)
+						this.coiRs = res.data.data.coiRs;
+						const rsNum = this.coiRs.length;
+						this.orgList = [];
+						for (let i=0; i<rsNum; i++) {
+							this.orgList[i] = {};
+							this.orgList[i].value = this.coiRs[i].coiId;
+							this.orgList[i].text = this.coiRs[i].coiName;
+							this.orgList[i].teamNum = this.coiRs[i].teamNum;
+						}
+						// console.log("orgList", this.orgList);
+						if (this.coiId > 0) {
+							this.orgChange(this.coiId, false);
+						}
+					},
+					fail: (err) => {
+						console.log("getOnlineMcSignUpDetail err", err)
+					},
+				});
+			},
+			// 线上赛报名(重新分组)
+			onlineMcSignUp() {
+				uni.request({
+					url: apiOnlineMcSignUp,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						mcId: this.mcId,
+						coiId: this.coiId,
+						selectTeam: this.teamNum
+					},
+					success: (res) => {
+						console.log("onlineMcSignUp", res)
+						
+						if (checkResCode(res)) {
+							uni.showToast({
+								title: '比赛报名成功!',
+								icon: 'none',
+								duration: 3000
+							});
+
+							uni.navigateTo({
+								url: '/pages/bm/style1/rankList?' + this.queryString
+							});
+						}
+					},
+					fail: (err) => {
+						console.log("onlineMcSignUp err", err)
+						uni.showToast({
+							title: '出错了,报名失败',
+							icon: 'none',
+							duration: 3000
+						});
+					},
+				});
+			},
+			btnBack() {
+				// console.log("from:", this.from)
+				if (this.from != '') {
+					window.history.back();
+				} else {
+					window.location.href = `action://to_home/`;
+				}
+			},
+			btnInfo() {
+				this.$refs.mypopup.popupOpen();
+			},
+			orgChange(value, resetTeamNum=true) {
+				console.log("[orgChange] value:", value);
+				if (resetTeamNum) {
+					this.teamNum = 0;
+				}
+				if (value > 0) {
+					const selectedOption = this.orgList.find(option => option.value === value);
+					const teamNum = selectedOption.teamNum;
+					// console.log("[orgChange] teamNum:", teamNum);
+					this.getTeamList(teamNum);
+				} else {
+					this.getTeamList(0);
+				}
+				
+			},
+			btnSignup() {
+				if (!checkToken(this.token)) {
+					return;
+				}
+				
+				if (!(this.coiId > 0)) {
+					uni.showToast({
+						title: '请选择组织名称',
+						icon: 'none',
+						duration: 2000
+					});
+					return;
+				}
+				// if ((this.teamList.length > 0) && !(this.teamNum > 0)) {
+				// 	uni.showToast({
+				// 		title: '请选择分组',
+				// 		icon: 'none',
+				// 		duration: 2000
+				// 	});
+				// 	return;
+				// }
+				this.coiName = tools.getSelectedText(this.orgList, this.coiId);
+				this.teamName = tools.getSelectedText(this.teamList, this.teamNum);
+				this.$refs.alertDialog.open();
+			},
+			dialogConfirm() {			
+				this.onlineMcSignUp();
+			},
+			dialogClose() {}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		width: 100vw;
+		height: 100vh;
+	}
+
+	.top-default {
+		width: 100%;
+		/* height: 35vh; */
+		height: 215px;
+		padding-top: 30px;
+		padding-bottom: 30px;
+		justify-content: space-between;
+		background-image: url("/static/backgroud/top_colorbar.png"), linear-gradient(180deg,#f8a95a 0%,#d25f11 100%);
+		/* background-image: url("/static/backgroud/top_colorbar.png"), linear-gradient(180deg,#7aedff 0%,#047200 100%); */
+		/* background-image: url("/static/backgroud/top_colorbar.png"), linear-gradient(180deg,#178bff 0%,#004d9b 100%); */
+		background-repeat: no-repeat;
+		background-position: center, 0px 0px;
+		background-size: auto 176px , cover;
+	}
+	
+	.logo-default {
+		width: 150px;
+		height: 150px;
+		margin-top: 10px;
+		background-image: url('/static/logo/jbs.png');
+		background-repeat: no-repeat;
+		background-position-x: center;
+		background-position-y: center;
+		background-size: contain;
+	}
+	
+	.topbar {
+		width: 90%;
+		padding: 0rpx 30rpx;
+		justify-content: space-between;
+	}
+
+	.topbar-back {
+		width: 43rpx;
+		height: 43rpx;
+		/* opacity: 0; */
+	}
+
+	.topbar-info {
+		width: 46rpx;
+		height: 46rpx;
+	}
+
+	.mcName {
+		color: white;
+		font-size: 40rpx;
+		font-weight: 550;
+	}
+
+	.toptime {
+		height: 65rpx;
+		margin-top: 20rpx;
+		padding: 0 50rpx;
+		justify-content: space-evenly;
+		background-color: white;
+		border-radius: 40rpx;
+	}
+
+	.acttime {
+		font-weight: 550;
+		color: #333333;
+		font-size: 30rpx;
+	}
+
+	.clock {
+		width: 30rpx;
+		height: 30rpx;
+		margin-right: 20rpx;
+	}
+
+	.main {
+		width: 76%;
+		/* height: 500rpx; */
+		margin-top: 80rpx;
+		justify-content: space-around;
+	}
+
+	.select {
+		margin-bottom: 36rpx;
+	}
+
+	.introduce {
+		margin-top: 10rpx;
+		margin-bottom: 80rpx;
+		align-items: flex-start;
+		justify-content: space-around;
+	}
+
+	.introduce-title {
+		color: #333333;
+		font-size: 30rpx;
+		line-height: 60rpx;
+	}
+
+	.introduce-content {
+		color: #333333;
+		font-size: 25rpx;
+		line-height: 36rpx;
+	}
+
+	.btnSignup {
+		width: 100%;
+		height: 100rpx;
+		margin-bottom: 30rpx;
+		color: white;
+		font-weight: bold;
+		line-height: 100rpx;
+		background-color: #2e85ec;
+		border-radius: 55rpx;
+	}
+
+	.dialog-content {
+		width: 279px;
+		height: 152px;
+		background: #f1f1f1;
+		border-radius: 9px;
+		justify-content: center;
+		text-align: center;
+		font-weight: 550;
+		color: #333333;
+	}
+
+	.dialog-content-1 {
+		font-size: 40rpx;
+		margin-bottom: 30rpx;
+	}
+
+	.dialog-content-2 {
+		font-size: 32rpx;
+		margin-bottom: 10rpx;
+	}
+</style>

+ 16 - 0
card/pages/index/index.vue

@@ -0,0 +1,16 @@
+<template>
+	<view></view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+			}
+		},
+		onLoad(event) { // 类型非必填,可自动推导
+		},
+		methods: {
+		}
+	}
+</script>

+ 96 - 0
card/pages/jbs/cardconfig.md

@@ -0,0 +1,96 @@
+卡片页面定制
+
+[小飞龙]
+
+"index": {
+	"css": "
+		.content-bg{
+			background: url('static/cardbg/xfl.png');
+			background-size: cover;
+		}
+		
+		.logo{
+			width: 120px;
+			height: 120px;
+			background: url('static/logo/xfl.png') no-repeat center;
+			background-size: contain;
+		}
+	"
+},
+
+"rankList": {
+	"css": "
+		.top{
+			width: 100%;
+			height: 26vh;
+			padding-top: 30px;
+			justify-content: space-between;
+			background-image: url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+			background-repeat: no-repeat;
+			background-position: center, 0px 0px;
+			background-size: auto 22vh , cover;
+		}
+
+		.logo{
+			width: 150px;
+			height: 150px;
+			margin-top: 10px;
+			background-image: url('static/logo/xfl.png');
+			background-repeat: no-repeat;
+			background-position-x: center;
+			background-position-y: center;
+			background-size: contain;
+		}
+	",
+}
+
+
+[山青世界]
+
+"index": {
+	"css": "
+		.logo{
+			width: 120px;
+			height: 120px;
+			background: url('static/logo/sqsj.png') no-repeat center;
+			background-size: contain;
+		}
+	"
+},
+
+"rankList": {
+	"css": "
+		.top{
+			width: 100%;
+			height: 26vh;
+			padding-top: 30px;
+			justify-content: space-between;
+			background-image: url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+			background-repeat: no-repeat;
+			background-position: center, 0px 0px;
+			background-size: auto 22vh , cover;
+		}
+		
+		.logo{
+			width: 150px;
+			height: 150px;
+			margin-top: 10px;
+			background-image: url('static/logo/xfl.png');
+			background-repeat: no-repeat;
+			background-position-x: center;
+			background-position-y: center;
+			background-size: contain;
+		}
+	",
+	"popupDataList": [
+		{
+			"type": 1,
+			"data": {
+				"title": "山青活动",
+				"img": "/static/logo/sqsj.png",
+				"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务"
+			}
+		}, 
+		"default"
+	],
+}

+ 331 - 0
card/pages/jbs/index.vue

@@ -0,0 +1,331 @@
+<!-- 
+锦标赛
+http://localhost:5173/card/#/pages/jbs/index
+https://oss-mbh5.colormaprun.com/card/#/pages/jbs/index
+ -->
+<template>
+	<view class="body body-radius">
+		<view class="content" :class="cssContentBg" @click="btnClick">
+			<view class="top uni-row">
+				<view class="top-right uni-row">
+					<image mode="aspectFit" class="clock" src="/static/default/clock.png"></image>
+					<view class="countdown">{{countdown}}</view>
+				</view>
+			</view>
+			<view class="main uni-column">
+				<view :class="cssLogo"></view>
+				<text class="type">{{type}}</text>
+				<text class="name">{{ecName}}</text>
+				<button class="button button-txtcolor">{{btnText}}</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import tools from '../../common/tools';
+	import {
+		token,
+		ossUrl,
+		apiCardBaseQuery,
+		apiCardConfigQuery,
+		apiCardDetailQuery
+	} from '../../common/api';
+
+	export default {
+		data() {
+			return {
+				pageName: "index",
+				queryString: "",
+				token: "",
+				
+				ecId: 0, // 卡片id
+				ecName: '', // 卡片名称
+				ecDesc: '', // 卡片简介
+				beginSecond: null, // 卡片开始时间戳,单位秒
+				endSecond: null, // 卡片结束时间戳,单位秒
+				
+				// mcId: 0, // 赛事id
+				// mcType: 0, // 赛事类型 1 普通活动 2 线下赛 3 线上赛
+				// mcName: "", // 赛事名称
+
+				countdown: "", // 倒计时
+				interval: null,
+				
+				cssContentBg: "",
+				cssLogo: "",
+				type: "",
+				btnText: "",
+			}
+		},
+		computed: {},
+		onLoad(event) { // 类型非必填,可自动推导
+			// console.log(event);
+			this.queryString = tools.objectToQueryString(event);
+			// console.log(queryString);
+
+			this.token = event["token"] ?? token;
+			this.ecId = event["id"] ?? 0;
+
+			this.type = event["type"] ?? "锦标赛";
+			this.btnText = event["btnText"] ?? "开始比赛";
+			
+			tools.removeCssCode();
+
+			this.getCardBaseQuery();
+			this.getCardConfigQuery();
+		},
+		onUnload() {
+			this.clear();
+		},
+		methods: {
+			clear() {
+				if (this.interval != null) {
+					clearInterval(this.interval);
+					this.interval = null;
+				}
+			},
+			loadConfig(config) {
+				// console.log("config", config);
+				const css = config.css;
+				if (css != undefined && css.length > 0) {
+					tools.loadCssCode(css);
+					
+					if (css.indexOf(".content-bg{") >= 0) {
+						this.cssContentBg = "content-bg";
+					}
+					if (css.indexOf(".logo{") >= 0) {
+						this.cssLogo = "logo";
+					}
+				}
+				
+				if (this.cssContentBg == "") {
+					this.cssContentBg = "content-bg-default";
+				}
+				if (this.cssLogo == "") {
+					this.cssLogo = "logo-default";
+				}
+				console.log("[loadConfig] cssContentBg:", this.cssContentBg);
+				console.log("[loadConfig] cssLogo:", this.cssLogo);
+			},
+			// 获取倒计时
+			getCountdown() {
+				// console.log(this.endSecond)
+				if (this.endSecond > 0) {
+					const now = Date.now() / 1000;
+					const dif = this.endSecond - now;
+					// const dif = 3600*24 - 60;
+					if (dif > 0) {
+						this.countdown = tools.convertSecondsToDHM(dif);
+					} else {
+						this.countdown = "已结束";
+					}
+					// this.countdown = tools.convertSecondsToHMS(dif);
+				} else {
+					this.countdown = "--天--小时";
+				}
+			},
+			// 卡片基本信息查询
+			getCardBaseQuery() {
+				uni.request({
+					url: apiCardBaseQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId
+					},
+					success: (res) => {
+						console.log("getCardBaseQuery", res)
+						const data = res.data.data;
+						
+						this.ecName = data.ecName;
+						this.ecDesc = data.ecDesc;
+						this.beginSecond = data.beginSecond;
+						this.endSecond = data.endSecond;
+						
+						this.getCountdown();
+						
+						this.clear();
+						this.interval = setInterval(this.getCountdown, 60000);
+					},
+					fail: (err) => {
+						console.log("getCardBaseQuery err", err)
+					},
+				});
+			},
+			// 卡片配置信息查询
+			getCardConfigQuery() {
+				uni.request({
+					url: apiCardConfigQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId,
+						pageName: this.pageName
+					},
+					success: (res) => {
+						// console.log("getCardConfigQuery", res)
+						const data = res.data.data;
+						const config = data.configJson != "" ? JSON.parse(data.configJson) : "";
+						// console.log("configJson", config);
+						/* const config = {
+						"css": `
+							.content-bg{
+								background: url('static/cardbg/xfl.png');
+								background-size: cover;
+							}
+							
+							.logo{
+								width: 120px;
+								height: 120px;
+								background-image: url('static/logo/xfl.png');
+								background-size: contain;
+							}
+						`
+						}; */
+						
+						this.loadConfig(config);
+					},
+					fail: (err) => {
+						console.log("getCardConfigQuery err", err)
+					},
+				});
+			},
+			// 卡片信息查询
+			// getCardDetailQuery() {
+			// 	uni.request({
+			// 		url: apiCardDetailQuery,
+			// 		header: {
+			// 			"Content-Type": "application/x-www-form-urlencoded",
+			// 			"token": this.token,
+			// 		},
+			// 		method: "POST",
+			// 		data: {
+			// 			ecId: this.ecId
+			// 		},
+			// 		success: (res) => {
+			// 			console.log("getCardDetailQuery", res)
+			// 			const data = res.data.data;
+			// 			this.mcType = data.mcType;
+			// 			this.mcId = data.mcId;
+			// 			this.mcName = data.mcName;
+			// 			this.endSecond = data.endSecond;
+
+			// 			this.getCountdown();
+
+			// 			this.clear();
+			// 			this.interval = setInterval(this.getCountdown, 60000);
+			// 		},
+			// 		fail: (err) => {
+			// 			console.log("getCardDetailQuery err", err)
+			// 		},
+			// 	});
+			// },
+			btnClick() {
+				// uni.reLaunch({
+				// uni.navigateTo({
+				// 	url: '/pages/default/rankList?' + this.queryString
+				// });
+				// window.location.href = `https://oss-mbh5.colormaprun.com/card/#/pages/default/rankList?${this.queryString}`;
+				window.location.href = `${ossUrl}#/pages/jbs/rankList?${this.queryString}`;
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		width: 100vw;
+		height: 100vh;
+	}
+
+	.content-bg-default {
+		background: linear-gradient(180deg, #7aedff 0%, #047200 100%);
+		/* background: linear-gradient(180deg, #178bff 0%, #004d9b 100%); */
+		/* background: linear-gradient(180deg, #7aedff 0%, #8d2219 100%); */
+	}
+
+	.top {
+		width: 100%;
+		justify-content: flex-end;
+	}
+
+	.top-right {
+		min-width: 180rpx;
+		height: 80rpx;
+		margin-top: 30rpx;
+		margin-right: 30rpx;
+		padding-left: 16rpx;
+		padding-right: 16rpx;
+		background-color: rgba(0, 0, 0, 0.3);
+		border-radius: 12px;
+	}
+
+	.clock {
+		width: 50rpx;
+		height: 50rpx;
+		margin-right: 12rpx;
+	}
+
+	.countdown {
+		min-width: 120rpx;
+		text-align: center;
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 46rpx;
+		/* letter-spacing: 2rpx; */
+	}
+
+	.main {
+		width: 100%;
+		height: 700rpx;
+		margin-top: 20rpx;
+		justify-content: space-evenly;
+	}
+
+	.logo-default {
+		width: 120px;
+		height: 120px;
+		background-image: url('/static/logo/jbs.png');
+		background-repeat: no-repeat;
+		background-position-x: center;
+		background-position-y: center;
+		background-size: contain;
+	}
+
+	.type {
+		opacity: 60%;
+		/* line-height: 25px; */
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 36rpx;
+		text-align: center;
+	}
+
+	.name {
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 46rpx;
+		text-align: center;
+	}
+
+	.button {
+		width: 320rpx;
+		height: 86rpx;
+		background: #ffffff;
+		border-radius: 56rpx;
+		font-size: 46rpx;
+		line-height: 80rpx;
+	}
+
+	.button-txtcolor {
+		color: #000000;
+	}
+	
+</style>

+ 456 - 0
card/pages/jbs/rankList.vue

@@ -0,0 +1,456 @@
+<!-- 
+锦标赛排名列表
+http://localhost:5173/card/#/pages/jbs/rankList
+https://oss-mbh5.colormaprun.com/card/#/pages/jbs/rankList
+ -->
+<template>
+	<view class="body">
+		<view class="content">
+			<view class="uni-column" :class="cssTop">
+				<view class="topbar uni-row">
+					<image mode="aspectFit" class="topbar-back" @click="btnBack" src="/static/default/back.png"></image>
+					<text class="mcName">{{mcName}}</text>
+					<image mode="aspectFit" class="topbar-info" @click="btnInfo" src="/static/default/info.png"></image>
+				</view>
+				<view :class="cssLogo"></view>
+				<view class="topcontent uni-row">
+					<text class="countdown">结束倒计时:</text>
+					<image mode="aspectFit" class="cal" src="/static/default/cal.png"></image>
+					<text class="countdown">{{countdown}}</text>
+				</view>
+				<!-- <text class="mcName">{{ecId}} - {{mcId}} - {{token}}</text> -->
+			</view>
+			<view class="main uni-column">
+				<my-ranklist :rankRs="rankList.totalRankRs"></my-ranklist>
+				<button class="btnStart" @click="btnStart">开始比赛</button>
+			</view>
+			
+			<my-popup ref="mypopup" :dataList="popupDataList"></my-popup>
+			
+		</view>
+	</view>
+</template>
+
+<script>
+	import tools from '../../common/tools';
+	import { defaultPopUpDataList } from '/common/define';
+	import {
+		token,
+		apiCardDetailQuery,
+		apiCardRankDetailQuery,
+		apiCardConfigQuery,
+		apiOnlineMcSignUp,
+		checkResCode
+	} from '../../common/api';
+
+	export default {
+		data() {
+			return {
+				pageName: "rankList",
+				firstEnterKey: 'firstEnter-jbs_rankList',
+				queryString: "",
+				token: "",
+				
+				ecId: 0, // 卡片id
+				coiId: 0, // 单位id
+				mcId: 0, // 赛事id
+				mcType: 0, // 赛事类型 1 普通活动 2 线下赛 3 线上赛
+				mcName: "", // 赛事名称
+				endSecond: null, // 赛事结束时间戳
+				
+				countdown: "", // 倒计时
+				rankList: { // 排名列表
+					totalRankRs: [],
+					teamRankRs: [],
+					inTeamRs: [],
+				},
+				interval: null,
+				
+				dispArrStr: "total", // 要显示的集合范围 (total,team,in,other)
+				cssTop: "",
+				cssLogo: "",
+				popupDataList: [],
+			}
+		},
+		computed: {},
+		onLoad(event) { // 类型非必填,可自动推导
+			// console.log(event);
+			this.queryString = tools.objectToQueryString(event);
+			// console.log(queryString);
+			this.token = event["token"] ?? token;
+			this.ecId = event["id"] ?? 0;
+			
+			tools.removeCssCode();
+
+			this.getCardConfigQuery();
+			// this.getCardDetailQuery();
+		},
+		// 页面初次渲染完成,此时组件已挂载完成,DOM 树($el)已可用
+		onReady() {
+			this.dealFirstEnter();
+		},
+		onUnload() {
+			this.clear();
+		},
+		methods: {
+			dealFirstEnter() {
+				let that = this;
+				uni.getStorage({
+					key: that.firstEnterKey,
+					success: (res) => {
+						console.log('[getStorage]', that.firstEnterKey, res.data);
+					},
+					fail: (e) => {
+						console.log('[getStorage] fail', that.firstEnterKey, e);
+						that.btnInfo();
+						that.setFirstEnterValue(true);
+					},
+				})
+			},
+			setFirstEnterValue(data) {
+				let that = this;
+				uni.setStorage({
+					key: that.firstEnterKey,
+					data: data,
+					success: () => {
+						console.log('[setStorage] success', that.firstEnterKey, data);
+					},
+					fail: (e) => {
+						console.log('[setStorage] fail', that.firstEnterKey, e);
+					},
+				})
+			},
+			clear() {
+				if (this.interval != null) {
+					clearInterval(this.interval);
+					this.interval = null;
+				}
+			},
+			loadConfig(config) {
+				// console.log("config", config);
+				
+				// 加载CSS样式
+				const css = config.css;
+				if (css != undefined && css.length > 0) {
+					tools.loadCssCode(css);
+					
+					if (css.indexOf(".top{") >= 0) {
+						this.cssTop = "top";
+					}
+					if (css.indexOf(".logo{") >= 0) {
+						this.cssLogo = "logo";
+					}
+				}
+				
+				if (this.cssTop == "") {
+					this.cssTop = "top-default";
+				}
+				if (this.cssLogo == "") {
+					this.cssLogo = "logo-default";
+				}
+				console.log("[loadConfig] cssTop:", this.cssTop);
+				console.log("[loadConfig] cssLogo:", this.cssLogo);
+				
+				// 加载弹窗数据
+				const popupDataList = config.popupDataList;
+				// console.log("[loadConfig] popupDataList:", popupDataList);
+				if (popupDataList != undefined && popupDataList.length > 0) {
+					for (var i = 0; i < popupDataList.length; i++) {
+						// console.log("[loadConfig] popupDataList", i, popupDataList[i]);
+						if (popupDataList[i] == 'default') {
+							for (var j = 0; j < defaultPopUpDataList.length; j++) {
+								this.popupDataList.push(defaultPopUpDataList[j]);
+							}
+						} else {
+							this.popupDataList.push(popupDataList[i]);
+						}
+					}
+				} else {
+					this.popupDataList = defaultPopUpDataList;
+					console.log("[loadConfig] popupDataList 加载默认列表");
+				}
+				// console.log("[loadConfig] popupDataList:", this.popupDataList);
+			},
+			// 获取倒计时
+			getCountdown() {
+				if (this.endSecond > 0) {
+					const now = Date.now() / 1000;
+					const dif = this.endSecond - now;
+					// const dif = 3600*24 - 60;
+					if (dif > 0) {
+						this.countdown = tools.convertSecondsToDHM(dif);
+					} else {
+						this.countdown = "已结束";
+					}
+					// this.countdown = tools.convertSecondsToHMS(dif);
+				} else {
+					this.countdown = "--天--小时";
+				}
+			},
+			getCardConfigQuery() {
+				uni.request({
+					url: apiCardConfigQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId,
+						pageName: this.pageName
+					},
+					success: (res) => {
+						console.log("getCardConfigQuery", res)
+						const data = res.data.data;
+						const config = data.configJson != "" ? JSON.parse(data.configJson) : "";
+						// console.log("configJson", data.configJson);
+						/* const config = {
+						"css": `
+							.top{
+								width: 100%;
+								height: 26vh;
+								padding-top: 30px;
+								justify-content: space-between;
+								background-image: url('static/backgroud/top_colorbar.png'), linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+								background-repeat: no-repeat;
+								background-position: center, 0px 0px;
+								background-size: auto 22vh , cover;
+							}
+							
+							.logo{
+								width: 150px;
+								height: 150px;
+								margin-top: 10px;
+								background-image: url('static/logo/xfl.png');
+								background-repeat: no-repeat;
+								background-position-x: center;
+								background-position-y: center;
+								background-size: contain;
+							}
+						`,
+						"popupDataList": [
+							{
+								"type": 1,
+								"data": {
+									"title": "山青活动",
+									"img": "/static/logo/sqsj.png",
+									"content": "山青世界为广大青少年提供了亲近自然、劳动实践、拓展培训、军事教育、科普体验、自然探索的平台和机会,也为企事业单位青年团队提供会议培训、拓展训练等服务",
+								}
+							}, 
+							"default"
+						]
+						}; */
+						
+						this.loadConfig(config);
+						this.getCardDetailQuery();
+					},
+					fail: (err) => {
+						console.log("getCardConfigQuery err", err)
+					},
+				});
+			},
+			// 卡片信息查询
+			getCardDetailQuery() {
+				uni.request({
+					url: apiCardDetailQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId
+					},
+					success: (res) => {
+						// console.log("getCardDetailQuery", res)
+						const data = res.data.data;
+						this.coiId = data.coiId;
+						this.mcId = data.mcId;
+						this.mcType = data.mcType;
+						this.mcName = data.mcName;
+						this.endSecond = data.endSecond;
+
+						this.getCountdown();
+						this.getCardRankDetailQuery();
+
+						this.clear();
+						this.interval = setInterval(this.getCountdown, 60000);
+					},
+					fail: (err) => {
+						console.log("getCardDetailQuery err", err)
+					},
+				});
+			},
+			// 排名查询
+			getCardRankDetailQuery() {
+				uni.request({
+					url: apiCardRankDetailQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						mcIdListStr: this.mcId,
+						mcType: this.mcType,
+						dispArrStr: this.dispArrStr
+					},
+					success: (res) => {
+						// console.log("getCardRankDetailQuery", res)
+						const rankdata = res.data.data;
+						this.rankList.totalRankRs = rankdata.totalRankRs;
+						this.rankList.teamRankRs = rankdata.teamRankRs;
+						this.rankList.inTeamRs = rankdata.inTeamRs;
+					},
+					fail: (err) => {
+						console.log("getCardRankDetailQuery err", err)
+					},
+				});
+			},
+			// 线上赛报名(重新分组)
+			onlineMcSignUp() {
+				uni.request({
+					url: apiOnlineMcSignUp,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						mcId: this.mcId,
+						coiId: this.coiId,
+						selectTeam: 0
+					},
+					success: (res) => {
+						console.log("onlineMcSignUp", res)
+
+						if (checkResCode(res)) {
+							// uni.showToast({
+							// 	title: '比赛报名成功!',
+							// 	icon: 'none',
+							// 	duration: 3000
+							// });
+
+							window.location.href =
+								`action://to_detail/?id=${this.mcId}&matchType=${this.mcType}`;
+						}
+					},
+					fail: (err) => {
+						console.log("onlineMcSignUp err", err)
+						uni.showToast({
+							title: '出错了,比赛报名失败',
+							icon: 'none',
+							duration: 3000
+						});
+					},
+				});
+			},
+			btnBack() {
+				// window.history.back();
+				window.location.href = `action://to_home/`;
+			},
+			btnStart() {
+				this.onlineMcSignUp();
+				// window.location.href = `action://to_detail/?id=${this.mcId}&matchType=${this.mcType}`;
+			},
+			btnInfo() {
+				this.$refs.mypopup.popupOpen();
+			},
+		}
+	}
+</script>
+
+<style>
+	.content {
+		width: 100vw;
+		height: 100vh;
+	}
+
+	.top-default {
+		width: 100%;
+		height: 29vh;
+		/* height: 500rpx; */
+		padding-top: 30px;
+		justify-content: flex-start;
+		background-image: url("/static/backgroud/top_bg.png");
+		background-repeat: no-repeat;
+		background-position-x: center;
+		background-position-y: center;
+		background-size: cover;
+	}
+
+	.logo-default {
+		/* width: 150px;
+		height: 150px;
+		margin-top: 10px;
+		background-image: url('/static/logo/jbs.png');
+		background-repeat: no-repeat;
+		background-position-x: center;
+		background-position-y: center;
+		background-size: contain; */
+	}
+	
+	.topbar {
+		width: 90%;
+		/* padding: 0rpx 30rpx; */
+		justify-content: space-between;
+	}
+
+	.topbar-back {
+		width: 43rpx;
+		height: 43rpx;
+		/* opacity: 0; */
+	}
+
+	.topbar-info {
+		width: 46rpx;
+		height: 46rpx;
+	}
+
+	.mcName {
+		color: white;
+		font-size: 32rpx;
+	}
+
+	.topcontent {
+		min-width: 300rpx;
+		height: 100rpx;
+		justify-content: space-evenly;
+	}
+
+	.countdown {
+		color: white;
+		font-size: 32rpx;
+	}
+
+	.cal {
+		width: 30rpx;
+		margin-right: 20rpx;
+	}
+
+	.main {
+		width: 100%;
+		height: 66vh;
+		justify-content: space-between;
+	}
+
+	.btnStart {
+		width: 70%;
+		height: 5vh;
+		/* height: 80rpx; */
+		margin-bottom: 1vh;
+		font-weight: bold;
+		line-height: 5vh;
+		background-color: #ffb40b;
+	}
+
+</style>
+
+<style lang="scss" scoped>
+
+	::v-deep .list {
+		height: 58vh;
+		margin-top: 5px;
+	}
+	
+</style>

+ 444 - 0
card/pages/mytz/detail.vue

@@ -0,0 +1,444 @@
+<!-- 
+每月挑战 - 详情
+http://localhost:5173/card/#/pages/mytz/detail
+https://oss-mbh5.colormaprun.com/card/#/pages/mytz/detail
+ -->
+<template>
+	<view class="body">
+		<view class="content uni-column">
+			<view class="top uni-column">
+				<view class="topbar uni-row">
+					<image mode="aspectFit" class="topbar-back" @click="btnBack" src="/static/default/back.png"></image>
+					<text class="mcName">{{ecName}}</text>
+					<image mode="aspectFit" class="topbar-info" @click="btnInfo" src="/static/default/info.png"></image>
+				</view>
+				<text class="toplogo">{{curMonth}}</text>
+				<text class="cardDesc">{{ecDesc}}</text>
+			</view>
+			<view class="main uni-column">
+				<view class="cupview uni-column">
+					<view class="cup" :style="getCupStyle('cup')">
+						<view class="cup-mask" :style="getCupStyle('cup-mask')"></view>
+					</view>
+					<text class="cup-text"><text style="font-weight: 600;">{{month}}月</text> |
+						{{realNum}}/{{targetNum}}</text>
+					<text class="cup-text2">{{progressText}}</text>
+				</view>
+				<button class="btnStart" @click="btnStart">{{btnText}}</button>
+			</view>
+		</view>
+
+		<uni-popup ref="popup" :mask-click="false" maskBackgroundColor="rgba(0, 0, 0, 0.6)">
+			<view class="popup">
+				<swiper ref="swiper" class="swiper" :current="swiperCurrent" @change="swiperChange"
+					:indicator-dots="false" indicator-active-color="rgba(46, 133, 236, 1)" :autoplay="false"
+					:interval="5000">
+					<swiper-item class="swiper-item uni-column">
+						<view class="introduce uni-column">
+							<text class="introduce-title">挑战赛规则</text>
+							<view>
+								<text class="introduce-sn">①</text>
+								<text class="introduce-content">选任意定向地图,完成4次挑战,你就胜利啦!</text>
+							</view>
+							<view>
+								<text class="introduce-sn">②</text>
+								<text class="introduce-content">挑战成功,会点亮你的电子奖杯!</text>
+							</view>
+							<view>
+								<text class="introduce-sn">③</text>
+								<text class="introduce-content">定向里程累计5千米,就有一枚奖章等你哦!</text>
+							</view>
+						</view>
+						<image class="main-pic" mode="aspectFit" src="/static/mytz/main_pic.png"></image>
+						<button class="swiper-item-button" @click="popupClose">确定</button>
+					</swiper-item>
+				</swiper>
+			</view>
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+	import tools from '../../common/tools';
+	import {
+		token,
+		apiCardBaseQuery,
+		apiCurrentMonthlyChallengeQuery,
+		checkResCode,
+		checkToken
+	} from '../../common/api';
+
+	export default {
+		data() {
+			return {
+				queryString: "",
+				token: "",
+				tokenValid: false,
+				ecId: 0, // 卡片id
+				ecName: '', // 卡片名称
+				ecDesc: '', // 卡片简介
+
+				month: '', // 月名称
+				realNum: 0, // 实际完赛次数
+				targetNum: 0, // 要求完赛次数
+
+				// mcType: 0, // 赛事类型 1 普通活动 2 线下赛 3 线上赛
+				// mcName: "", // 赛事名称
+				swiperCurrent: 0, // swiper当前所在滑块的 index
+				cupProgress: 100, // 奖杯进度 (100 - 实际进度,初始值为100)
+				progressText: "",
+				btnText: "",
+			}
+		},
+		computed: {
+			curMonth() {
+				var currentDate = new Date();
+				var currentMonth = currentDate.getMonth() + 1; // 月份从0开始,所以要加1
+
+				return `${currentMonth}月`;
+			},
+
+		},
+		onLoad(event) { // 类型非必填,可自动推导
+			// console.log(event);
+			this.queryString = tools.objectToQueryString(event);
+			// console.log(queryString);
+			this.token = event["token"] ?? token;
+			this.ecId = event["id"] ?? 0;
+
+			this.getCardBaseQuery();
+			this.getCurrentMonthlyChallengeQuery();
+		},
+		// 页面初次渲染完成,此时组件已挂载完成,DOM 树($el)已可用
+		onReady() {},
+		onUnload() {},
+		methods: {
+			getCupProgress() {
+				if (this.targetNum > 0 && this.realNum > 0) {
+					if (this.realNum < this.targetNum) {
+						const progress = this.realNum / this.targetNum * 100;
+						this.cupProgress = 100 - progress;
+					} else {
+						this.cupProgress = 0;
+					}
+				} else {
+					this.cupProgress = 100;
+				}
+				// console.log("cupProgress:", this.cupProgress);
+			},
+			getText() {
+				if (!this.tokenValid) {
+					this.progressText = `您尚未登录,欢迎参加挑战!!`;
+					this.btnText = "开始挑战";
+				}
+				else if (this.realNum == 0) {
+					this.progressText = `您尚未开始,欢迎参加挑战!!`;
+					this.btnText = "开始挑战";
+				}
+				else if (this.realNum > 0 && this.realNum < this.targetNum) {
+					this.progressText = `您已完成了${this.realNum}次挑战,继续加油!!`;
+					this.btnText = "继续挑战";
+				} else if (this.realNum > 0 && this.realNum >= this.targetNum){
+					this.progressText = `恭喜,挑战成功!!`;
+					this.btnText = "继续挑战";
+				} else {
+					this.progressText = ``;
+					this.btnText = "开始挑战";
+				}
+			},
+			getCupStyle(type) {
+				if (! (this.month > 0)) {
+					return '';
+				}
+				
+				let group = 1;
+				if (type == 'cup') {
+					return `background-image: url("static/cup/${group}/00${this.month}.png")`;
+				} else if (type == 'cup-mask') {
+					return `background-image: url("static/cup/${group}/00${this.month}h.png"); height:${this.cupProgress}% ;`;
+				}
+			},
+			// 卡片基本信息查询
+			getCardBaseQuery() {
+				uni.request({
+					url: apiCardBaseQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId
+					},
+					success: (res) => {
+						// console.log("getCardBaseQuery", res)
+						const data = res.data.data;
+
+						this.ecName = data.ecName;
+						this.ecDesc = data.ecDesc;
+					},
+					fail: (err) => {
+						console.log("getCardBaseQuery err", err)
+					},
+				});
+			},
+			// 玩家当前月挑战记录查询
+			getCurrentMonthlyChallengeQuery() {
+				uni.request({
+					url: apiCurrentMonthlyChallengeQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {},
+					success: (res) => {
+						console.log("getCurrentMonthlyChallengeQuery", res);
+						
+						if (checkResCode(res)) {
+							if (res.statusCode == 401) { // 未登录
+								this.tokenValid = false;
+							} else {
+								this.tokenValid = true;
+							}
+							
+							const data = res.data.data;
+
+							this.month = data.month;
+							this.realNum = data.realNum;
+							this.targetNum = data.targetNum;
+							
+							this.getCupProgress();
+							this.getText();
+						}
+					},
+					fail: (err) => {
+						console.log("getCurrentMonthlyChallengeQuery err", err)
+					},
+				});
+			},
+			btnBack() {
+				// window.history.back();
+				window.location.href = `action://to_home/`;
+			},
+			btnInfo() {
+				this.swiperCurrent = 0;
+				this.$refs.popup.open();
+			},
+			btnStart() {
+				window.location.href = `action://to_map_list/`;
+			},
+			//当前轮播索引
+			swiperChange(e) {
+				const curIndex = e.detail.current;
+				this.swiperCurrent = curIndex;
+			},
+			swiperNext() {
+				this.swiperCurrent++;
+			},
+			popupClose() {
+				this.$refs.popup.close()
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		width: 100vw;
+		height: 100vh;
+	}
+
+	.top {
+		width: 100%;
+		height: 360rpx;
+		padding-top: 60rpx;
+		padding-bottom: 60rpx;
+		justify-content: space-between;
+		background-image: url("/static/backgroud/top_run.png"), linear-gradient(180deg, #178bff 0%, #004d9b 100%);
+		background-repeat: no-repeat;
+		background-position: 50rpx 50rpx, 0rpx 0rpx;
+		/* background-position-x: center; */
+		/* background-position-y: center; */
+		background-size: auto 430rpx, contain;
+	}
+
+	.topbar {
+		width: 90%;
+		padding: 0rpx 30rpx;
+		justify-content: space-between;
+	}
+
+	.topbar-back {
+		width: 43rpx;
+		height: 43rpx;
+		/* opacity: 0; */
+	}
+
+	.topbar-info {
+		width: 46rpx;
+		height: 46rpx;
+		/* opacity: 0; */
+	}
+
+	.mcName {
+		color: white;
+		font-size: 40rpx;
+		font-weight: 550;
+	}
+
+	.toplogo {
+		width: 180rpx;
+		height: 180rpx;
+		/* margin-top: 20rpx; */
+		background: url("/static/mytz/month_bg.png") no-repeat center;
+		background-size: contain;
+		color: #ff870d;
+		font-size: 24px;
+		font-weight: 700;
+		text-align: center;
+		line-height: 220rpx;
+	}
+
+	.cardDesc {
+		/* font-weight: 550; */
+		color: white;
+		font-size: 32rpx;
+	}
+
+	.main {
+		width: 80%;
+		/* height: 800rpx; */
+		flex-grow: 0.5;
+		justify-content: space-between;
+	}
+
+	.cupview {
+		height: 500rpx;
+		margin-top: 150rpx;
+	}
+	
+	.cup {
+		width: 320rpx;
+		height: 320rpx;
+		/* background-image: url("/static/cup/004.png"); */
+		background-position-x: center;
+		background-repeat: no-repeat;
+		background-size: 300rpx auto;
+	}
+
+	.cup-mask {
+		width: 320rpx;
+		/* height: 30%; */
+		/* background-image: url("/static/cup/004h.png"); */
+		background-position-x: center;
+		background-repeat: no-repeat;
+		background-size: 300rpx auto;
+		overflow: hidden;
+		/* filter: grayscale(1); */
+	}
+
+	.cup-text {
+		color: #818181;
+		font-size: 28rpx;
+		line-height: 100rpx;
+	}
+
+	.cup-text2 {
+		color: #333333;
+		font-size: 28rpx;
+		line-height: 100rpx;
+	}
+
+	.introduce {
+		width: 80%;
+		align-items: flex-start;
+		justify-content: space-around;
+	}
+
+	.introduce-title {
+		margin-top: 70rpx;
+		margin-bottom: 60rpx;
+		width: 100%;
+		color: #333333;
+		font-size: 36rpx;
+		font-weight: 700;
+		/* line-height: 60rpx; */
+		text-align: center;
+	}
+
+	.introduce-sn {
+		margin-right: 10rpx;
+		color: #a16221;
+		font-size: 38rpx;
+		font-weight: 550;
+	}
+
+	.introduce-content {
+		color: #333333;
+		font-size: 28rpx;
+		line-height: 72rpx;
+	}
+
+	.main-pic {
+		width: 90%;
+		height: 200rpx;
+	}
+
+	.btnStart {
+		width: 100%;
+		height: 100rpx;
+		margin-bottom: 30rpx;
+		color: white;
+		font-weight: bold;
+		line-height: 100rpx;
+		background-color: #2e85ec;
+		border-radius: 55rpx;
+	}
+
+	.popup {
+		width: 90vw;
+		height: 900rpx;
+		background-color: #FEFBF6;
+		border-radius: 50rpx;
+	}
+
+	.swiper {
+		height: 100%;
+	}
+
+	.swiper-item {
+		/* height: 80vh; */
+		justify-content: space-between;
+		/* background-color: lightblue; */
+	}
+
+	.swiper-item-title {
+		margin-top: 80rpx;
+		font-size: 40rpx;
+	}
+
+	.swiper-item-image {
+		height: 300rpx;
+	}
+
+	.swiper-item-content {
+		height: 100rpx;
+		justify-content: start;
+	}
+
+	.swiper-item-button {
+		width: 80%;
+		height: 106rpx;
+		margin-bottom: 80rpx;
+		color: #ffffff;
+		/* font-weight: bold; */
+		line-height: 106rpx;
+		background-color: #2e85ec;
+		border-radius: 27px;
+	}
+
+	::v-deep .uni-swiper-dots-horizontal {
+		bottom: 230rpx;
+	}
+</style>

+ 167 - 0
card/pages/mytz/index.vue

@@ -0,0 +1,167 @@
+<!-- 
+每月挑战
+http://localhost:5173/card/#/pages/mytz/index
+https://oss-mbh5.colormaprun.com/card/#/pages/mytz/index
+ -->
+<template>
+	<view class="body body-radius">
+		<view class="content uni-column" :class="contentBg" @click="btnClick">
+			<view class="main uni-column">
+				<image mode="aspectFit" class="logo" :src="logoSrc"></image>
+				<text class="type">{{ecName}}</text>
+				<text class="name">{{name}}</text>
+				<button class="button button-txtcolor">{{btnText}}</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import tools from '../../common/tools';
+	import {
+		ossUrl,
+		token,
+		apiCardBaseQuery,
+	} from '../../common/api';
+	 
+	export default {
+		data() {
+			return {
+				queryString: "",
+				token: "",
+				
+				ecId: 0,	// 卡片id
+				ecName: '', // 卡片名称
+				ecDesc: '', // 卡片简介
+				beginSecond: null, // 卡片开始时间戳,单位秒
+				endSecond: null, // 卡片结束时间戳,单位秒
+				
+				contentBg: "",
+				logoSrc: "",
+				type: "",
+				name: "",
+				btnText: "",
+			}
+		},
+		computed: {
+			curYearMonth() {
+				var currentDate = new Date();
+				var currentYear = currentDate.getFullYear();
+				var currentMonth = currentDate.getMonth() + 1; // 月份从0开始,所以要加1
+				 
+				return `${currentYear}年${currentMonth}月`;
+			}
+		},
+		onLoad(event) { // 类型非必填,可自动推导
+			// console.log(event);
+			this.queryString = tools.objectToQueryString(event);
+			// console.log(queryString);
+			
+			this.token = event["token"] ?? token;
+			this.ecId = event["id"] ?? 0;
+			
+			this.contentBg = "content-bg-" + (event["bg"] ?? "blue");
+			this.logoSrc = "/static/logo/" + (event["logo"] ?? "mytz") + '.png';
+			// this.type = event["type"] ?? "每月挑战";
+			this.name = event["name"] ?? this.curYearMonth;
+			this.btnText = event["btnText"] ?? "开始挑战";
+			
+			this.getCardBaseQuery();
+		},
+		onUnload() {
+		},
+		methods: {
+			// 卡片基本信息查询
+			getCardBaseQuery() {
+				uni.request({
+					url: apiCardBaseQuery,
+					header: {
+						"Content-Type": "application/x-www-form-urlencoded",
+						"token": this.token,
+					},
+					method: "POST",
+					data: {
+						ecId: this.ecId
+					},
+					success: (res) => {
+						// console.log("getCardBaseQuery", res)
+						const data = res.data.data;
+						
+						this.ecName = data.ecName;
+						this.ecDesc = data.ecDesc;
+						this.beginSecond = data.beginSecond;
+						this.endSecond = data.endSecond;
+					},
+					fail: (err) => {
+						console.log("getCardBaseQuery err", err)
+					},
+				});
+			},
+			btnClick() {
+				window.location.href = `${ossUrl}#/pages/mytz/detail?${this.queryString}`;
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		width: 100vw;
+		height: 100vh;
+		justify-content: center;
+	}
+	
+	.content-bg-blue {
+		background: linear-gradient(180deg,#178bff 0%,#004d9b 100%);
+	}
+	
+	.content-bg-green {
+		background: linear-gradient(180deg,#7aedff 0%,#047200 100%);
+	}
+	
+	.content-bg-brown {
+		background: linear-gradient(180deg,#ad4301 0%,#8d2219 100%);
+	}
+
+	.main {
+		width: 100%;
+		height: 700rpx;
+		/* margin-top: 20rpx; */
+		justify-content: space-evenly;
+	}
+
+	.logo {
+		/* width: 230rpx; */
+		height: 230rpx;
+	}
+	
+	.type {
+		opacity: 60%;
+		/* line-height: 25px; */
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 36rpx;
+		text-align: center;
+	}
+	
+	.name {
+		font-family: Roboto;
+		color: #ffffff;
+		font-size: 46rpx;
+		text-align: center;
+	}
+	
+	.button {
+		width: 320rpx;
+		height: 86rpx;
+		background: #ffffff;
+		border-radius: 56rpx;
+		font-size: 46rpx;
+		line-height: 80rpx;
+	}
+	
+	.button-txtcolor {
+		color: #0458ad;
+	}
+	
+</style>

BIN
card/static/backgroud/top_bg.png


BIN
card/static/backgroud/top_bg1.png


BIN
card/static/backgroud/top_colorbar.png


BIN
card/static/backgroud/top_run.png


BIN
card/static/cardbg/xfl.png


BIN
card/static/common/hdlc.png


BIN
card/static/common/jbbs.png


BIN
card/static/cup/1/001.png


BIN
card/static/cup/1/001h.png


BIN
card/static/cup/1/002.png


BIN
card/static/cup/1/002h.png


BIN
card/static/cup/1/003.png


BIN
card/static/cup/1/003h.png


BIN
card/static/cup/1/004.png


BIN
card/static/cup/1/004h.png


BIN
card/static/cup/1/005.png


BIN
card/static/cup/1/005h.png


BIN
card/static/cup/1/006.png


BIN
card/static/cup/1/006h.png


BIN
card/static/cup/1/007.png


BIN
card/static/cup/1/007h.png


BIN
card/static/cup/1/008.png


BIN
card/static/cup/1/008h.png


BIN
card/static/cup/1/009.png


BIN
card/static/cup/1/009h.png


BIN
card/static/cup/1/010.png


BIN
card/static/cup/1/010h.png


BIN
card/static/cup/1/011.png


BIN
card/static/cup/1/011h.png


BIN
card/static/cup/1/012.png


BIN
card/static/cup/1/012h.png


BIN
card/static/default/back.png


BIN
card/static/default/cal.png


BIN
card/static/default/clock.png


BIN
card/static/default/info.png


BIN
card/static/default/medal_copper.png


BIN
card/static/default/medal_gold.png


BIN
card/static/default/medal_other.png


BIN
card/static/default/medal_silver.png


BIN
card/static/logo.png


BIN
card/static/logo/jbs.png


BIN
card/static/logo/mytz.png


BIN
card/static/logo/poly.png


BIN
card/static/logo/sddx.png


BIN
card/static/logo/sqsj.png


BIN
card/static/logo/xfl.png


BIN
card/static/medal/100km.png


BIN
card/static/medal/10km.png


BIN
card/static/medal/110km.png


BIN
card/static/medal/120km.png


BIN
card/static/medal/130km.png


BIN
card/static/medal/140km.png


BIN
card/static/medal/150km.png


BIN
card/static/medal/15km.png


BIN
card/static/medal/160km.png


BIN
card/static/medal/170km.png


BIN
card/static/medal/180km.png


BIN
card/static/medal/190km.png


BIN
card/static/medal/20km.png


BIN
card/static/medal/25km.png


BIN
card/static/medal/30km.png


BIN
card/static/medal/35km.png


BIN
card/static/medal/40km.png


BIN
card/static/medal/45km.png


BIN
card/static/medal/50km.png


BIN
card/static/medal/55km.png


BIN
card/static/medal/5km.png


BIN
card/static/medal/60km.png


BIN
card/static/medal/65km.png


BIN
card/static/medal/70km.png


BIN
card/static/medal/75km.png


BIN
card/static/medal/80km.png


BIN
card/static/medal/85km.png


BIN
card/static/medal/90km.png


BIN
card/static/medal/95km.png


BIN
card/static/medal/copper.png


BIN
card/static/medal/egg.png


BIN
card/static/medal/finished.png


BIN
card/static/medal/gold.png


BIN
card/static/medal/silver.png


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov