lvxinghai 4 年之前
父节点
当前提交
ff6491c0e5

+ 82 - 0
.gitignore

@@ -0,0 +1,82 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Typescript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# next.js build output
+.next
+
+# editor configs
+.idea
+
+# unpackage folders
+unpackage
+
+# unpackage folders
+unpackage/dist
+# build folders
+build
+
+# git
+git
+git.pub
+
+.vscode
+android/
+#.vscode
+*.log
+build/**/
+.build
+.history/
+unpackage/

+ 54 - 0
App.vue

@@ -0,0 +1,54 @@
+<script>
+	export default {
+		onLaunch: function() {
+			// console.log("App Launch");
+			this.checkArguments(); // 检测启动参数
+			if (uni.getSystemInfoSync().platform == 'android') {
+				// 重点是以下: 一定要监听后台恢复 !一定要
+				plus.globalEvent.addEventListener('newintent', e => {
+					this.checkArguments(); // 检测启动参数
+				});
+			}
+		},
+		onShow: function() {
+			// console.log("App Show");
+		},
+		onHide: function() {
+			// console.log("App Hide");
+		},
+		methods: {
+			// 监测是否传参数
+			checkArguments() {
+				try {
+					if (uni.getSystemInfoSync().platform === 'android') {
+						// 接收第三方app传递的参数 extra;
+						if (plus.runtime.arguments) {
+              // patParams: 院区,病区, 床号
+              const { patParams } = JSON.parse(plus.runtime.arguments);
+              uni.setStorageSync('patParams', patParams);
+						}
+					}
+				} catch (e) {}
+			}
+		}
+	};
+</script>
+
+<style lang="less">
+	/*每个页面公共css */
+	body,
+	uni-app,
+	uni-page,
+	uni-page-wrapper,
+	uni-page-body {
+		height: 100%;
+		font-size: 12.5rpx;
+		line-height: 18.75rpx;
+	}
+
+	view,
+	label,
+	scroll-view {
+		box-sizing: border-box;
+	}
+</style>

+ 8 - 3
README.md

@@ -1,4 +1,9 @@
-## web_TracerMethodology
-
-追踪方法学前端
+### 项目结构:
+---
+1. 公共样式写在App.vue;
 
+### 公共组件的注册方式 
+---
+ 1. 在main.js中全局注册;
+ 2. 在components目录下创建文件夹=文件名的组件,会自动全局注册,但是需要注意:在模板中使用的名字也必须与定义的名字一致,比如定义了n-navigation/n-navigation.vue组件,必须是<n-navigation></n-navigation>;因为标签推荐kebab-case方式,所以定义的公共组件也推荐这种命名方式
+ 

+ 140 - 0
components/date-time-pick-group/date-time-pick-group.vue

@@ -0,0 +1,140 @@
+<template>
+	<view class="date-pick">
+		<text class="label" v-show="label">{{ label }}</text>
+		<view class="date-box">
+		  <date-time-picker
+			  :fields="fields"
+			  :start="dateObj.startTime"
+			  :end="dateObj.endTime"
+			  :defaultValue="dateObj.startSelectValue"
+        :pickType="'start'"
+        placeholder="开始日期时间"
+			  @change="bindChange" />
+				 <text class="inner">~</text>
+			<date-time-picker
+				:fields="fields"
+				:start="dateObj.startTime"
+				:end="dateObj.endTime"
+				:defaultValue="dateObj.endSelectValue"
+        :pickType="'end'"
+        placeholder="结束日期时间"
+				@change="bindChange">
+        <!-- <image src="/static/date-icon.png" class="date-icon"></image> -->
+			</date-time-picker>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 日期时间选择控件(开始时间~结束时间)
+ */
+import DateTimePicker from '@/components/date-time-picker/date-time-picker.vue';
+import moment from 'moment';
+// 时间格式化对象 (目前只支持短横线(-)的格式如: 'YYYY-MM-DD HH:mm:ss', 不支持斜线格式如 / )
+const FORMATS = {
+  year: 'YYYY',
+  month: 'YYYY-MM',
+  day: 'YYYY-MM-DD',
+  hour: 'YYYY-MM-DD HH',
+  minute: 'YYYY-MM-DD HH:mm',
+  second: 'YYYY-MM-DD HH:mm:ss',
+};
+export default {
+	components: {DateTimePicker},
+	props: {
+		label: {
+			type: String,
+			default: ''
+    },
+    // 时间选择器的粒度 有效值:year | month | day | hour | minute
+		fields: {
+			type: String,
+			default: 'minute'
+		},
+		// 允许选中的最小值
+		startTime: {
+			type: String,
+			default: "1970-01-01 00:00"
+		},
+		// 允许选中的最大值
+		endTime: {
+			type: String,
+			default: "2300-01-01 00:00"
+		},
+		// 开始 选中的时间
+		startSelectValue: {
+			type: String,
+			default: ''
+		},
+		// 结束 选中的时间
+		endSelectValue: {
+			type: String,
+			default: ''
+		}
+  },
+  computed: {
+    dateObj(){
+      const {fields, startTime, endTime, startSelectValue, endSelectValue} = this;
+      if(fields){
+        return {
+          startTime: startTime ? moment(new Date(startTime)).format(FORMATS[fields]) : '1970-01-01 00:00',
+          endTime: endTime ? moment(new Date(endTime)).format(FORMATS[fields]) : '2300-01-01 00:00',
+          startSelectValue: startSelectValue ? moment(new Date(startSelectValue)).format(FORMATS[fields]) : '',
+          endSelectValue: endSelectValue ? moment(new Date(endSelectValue)).format(FORMATS[fields]) : ''
+        }
+      }else {
+        return {
+          startTime,
+          endTime,
+          startSelectValue,
+          endSelectValue
+        }
+      }
+    }
+  },
+	methods: {
+		bindChange(valObj, pickType){
+      this.$emit('changeDate', valObj, pickType)
+		}
+	}
+};
+</script>
+
+<style lang="less">
+.date-pick {
+	display: flex;
+	align-items: center;
+	height: 25rpx;
+
+	.label {
+		margin-right: 3.12rpx;
+		font-size: 12.5rpx;
+		font-weight: 500;
+		color: #17181a;
+	}
+
+	.date-box {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		width: 238.28rpx;
+		height: 25rpx;
+		background: #ffffff;
+		border-radius: 3.12rpx;
+		border: 0.78rpx solid #e1e4ed;
+		color: #2e2f33;
+		box-sizing: border-box;
+
+
+		.date-icon {
+			width: 10.93rpx;
+			height: 10.93rpx;
+    }
+
+    .inner {
+      margin-right: 3.5rpx;
+    }
+	}
+}
+</style>

+ 82 - 0
components/date-time-pick/date-time-pick.vue

@@ -0,0 +1,82 @@
+<template>
+	<view class="date-pick-select">
+    <date-time-picker
+      :fields="fields"
+      :start="startTime"
+      :end="endTime"
+      :defaultValue="defaultValue"
+      @change="bindChange">
+       <image class="date-icon" src="/static/date-icon.png"></image>
+    </date-time-picker>
+	</view>
+</template>
+
+<script>
+/**
+ * 日期时间选择器(单个)
+ */
+import DateTimePicker from '@/components/date-time-picker/date-time-picker.vue';
+export default {
+  components: {DateTimePicker},
+	props: {
+		label: {
+			type: String,
+			default: ''
+    },
+    // 时间选择器的粒度 有效值:year | month | day | hour | minute
+		fields: {
+			type: String,
+			default: 'minute'
+		},
+		// 允许选中的最小值
+		startTime: {
+			type: String,
+			default: ''
+		},
+		// 允许选中的最大值
+		endTime: {
+			type: String,
+			default: ''
+		},
+		// 选中的时间
+		defaultValue: {
+			type: String,
+			default: ''
+		},
+		// 占位符
+		placeholder: {
+			type: String,
+			default: '日期时间'
+		},
+  },
+	methods: {
+    bindChange(dateObj){
+      this.$emit('change', dateObj)
+    }
+  }
+};
+</script>
+
+<style lang="less">
+.date-pick-select {
+	display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-width: 203.12rpx;
+  height: 25rpx;
+  border: 0.78rpx solid #E1E4ED;
+  border-radius: 3.12rpx;
+
+  .uni-input {
+    min-width: 101.56rpx;
+    font-size: 12.5rpx;
+    color: #2E2F33;
+  }
+
+  .date-icon {
+    margin-left: 7.81rpx;
+    width: 10.93rpx;
+    height: 10.93rpx;
+  }
+}
+</style>

+ 550 - 0
components/date-time-picker/date-time-picker.vue

@@ -0,0 +1,550 @@
+<template>
+	<view class="date-time">
+		<picker mode="multiSelector" :range="range" range-key="text" @change="change" @columnchange="columnchange" :value="value" :disabled="disabled">
+			<view class="content" :class="{ placeholder: !dateStr }">
+				<text>{{ dateStr ? dateStr : placeholder }}</text>
+        <slot></slot>
+			</view>
+		</picker>
+	</view>
+</template>
+
+<script>
+/**
+ * 日期时间选择器
+*/
+import utils from "./utils.js";
+import moment from "moment";
+export default {
+  /**
+   * 数据
+   */
+  props: {
+    /**
+     * picker类型 start: 开始时间;end:结束时间
+     */
+    pickType: {
+      type: String,
+      default: ""
+    },
+    // 是否禁用
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+
+    // 占位符
+    placeholder: {
+      type: String,
+      default: "请选择日期时间"
+    },
+
+    // 表示有效日期时间范围的开始,
+    // 字符串格式为 "YYYY-MM-DD hh:mm"
+    start: {
+      type: String,
+      default: "1970-01-01 00:00"
+    },
+
+    // 表示有效日期时间范围的结束
+    // 字符串格式为 "YYYY-MM-DD hh:mm"
+    end: {
+      type: String,
+      default: "2300-01-01 00:00"
+    },
+
+    // 表示选择器的粒度,有效值:year | month | day | hour | minute
+    fields: {
+      type: String,
+      default: "minute"
+    },
+
+    // 默认值
+    // 字符串格式为 "YYYY-MM-DD hh:mm"
+    defaultValue: {
+      type: String,
+      default: ""
+    }
+  },
+
+  /**
+   * 数据
+   */
+  data() {
+    return {
+      range: [],
+      value: [],
+      dateStr: "", // 最终显示的字符串
+      dtStart: null, // 有效范围开始
+      dtEnd: null // 有效范围结束
+    };
+  },
+
+  /**
+   * 监听数据
+   */
+  watch: {
+    // 默认值
+    defaultValue() {
+      // 设置默认值
+      this.setDefaultValue();
+    },
+    start(){
+       if(newVal != oldVal){
+        this.setDisabled()
+      }
+    },
+    end(newVal, oldVal){
+      if(newVal != oldVal){
+        this.setDisabled()
+      }
+    }
+  },
+  /**
+   * 组件初次加载完成
+   */
+  mounted() {
+    this.setDisabled();
+    // 设置默认值
+    this.setDefaultValue();
+  },
+  /**
+   * 方法
+   */
+  methods: {
+    // 设置有效日期开始和结束
+    setDisabled() {
+      let start = this.start;
+      let end = this.end;
+      // 验证是否是有效的开始和结束日期
+      if (!utils.isString(this.start)) {
+        // console.log('开始日期需为String类型,格式为 "YYYY-MM-DD hh:mm"');
+        start = "1970-1-1 00:00";
+      }
+      if (!utils.isString(this.end)) {
+        // console.log('结束日期需为String类型,格式为 "YYYY-MM-DD hh:mm"');
+        end = "2300-1-1 00:00";
+      }
+      // 将开始日期和结束日期转为 Date
+      let dtStart = utils.formatDate(start).dt;
+      let dtEnd = utils.formatDate(end).dt;
+      // 判断有效日期结束是否大于有效日期开始,如果不是,则将有效日期结束修改为有效日期开始往后300年
+      if (dtEnd <= dtStart) {
+        dtEnd = utils.formatDate(start).dt;
+        dtEnd.setFullYear(dtStart.getFullYear() + 300);
+        dtEnd.setDate(dtEnd.getDate() - 1);
+      }
+      // 更新开始日期和结束日期
+      this.dtStart = dtStart;
+      this.dtEnd = dtEnd;
+    },
+    /**
+     * 确认选择
+     */
+    change(event) {
+      let year, month, day, hour, minute;
+      if (this.fields == "year") {
+        year = this.range[0][this.value[0]].number; // 年
+        let dtStr = `${year}`;
+        this.setDateStr(dtStr);
+        this.$emit("change", utils.formatDate(dtStr), this.pickType);
+        return;
+      } else if (this.fields == "month") {
+        year = this.range[0][this.value[0]].number; // 年
+        month = this.range[1][this.value[1]].number; // 月
+        let dtStr = `${year}-${month}`;
+        this.setDateStr(dtStr);
+        this.$emit("change", utils.formatDate(dtStr), this.pickType);
+        return;
+      } else if (this.fields == "day") {
+        year = this.range[0][this.value[0]].number; // 年
+        month = this.range[1][this.value[1]].number; // 月
+        day = this.range[2][this.value[2]].number; // 日
+        let dtStr = `${year}-${month}-${day}`;
+        this.setDateStr(dtStr);
+        this.$emit("change", utils.formatDate(dtStr), this.pickType);
+        return;
+      } else if (this.fields == "hour") {
+        year = this.range[0][this.value[0]].number; // 年
+        month = this.range[1][this.value[1]].number; // 月
+        day = this.range[2][this.value[2]].number; // 日
+        hour = this.range[3][this.value[3]].number; // 时
+        day = this.range[2][this.value[2]].number; // 日
+        let dtStr = `${year}-${month}-${day} ${hour}`;
+        this.setDateStr(dtStr);
+        this.$emit("change", utils.formatDate(dtStr), this.pickType);
+        return;
+      } else if (this.fields == "minute") {
+        year = this.range[0][this.value[0]].number; // 年
+        month = this.range[1][this.value[1]].number; // 月
+        day = this.range[2][this.value[2]].number; // 日
+        hour = this.range[3][this.value[3]].number; // 时
+        minute = this.range[4][this.value[4]].number; // 分
+        let dtStr = `${year}-${month}-${day} ${hour}:${minute}`;
+        this.setDateStr(dtStr);
+        this.$emit("change", utils.formatDate(dtStr), this.pickType);
+        return;
+      }
+    },
+
+    /**
+     * 设置显示的值
+     * @param {Date|String} date 日期字符串或日期对象
+     */
+    setDateStr(date) {
+      let dt = utils.formatDate(date);
+      if (this.fields == "year") {
+        this.dateStr = `${dt.YYYY}`;
+        return;
+      }
+      if (this.fields == "month") {
+        this.dateStr = `${dt.YYYY}-${dt.MM}`;
+        return;
+      }
+      if (this.fields == "day") {
+        this.dateStr = `${dt.YYYY}-${dt.MM}-${dt.DD}`;
+        return;
+      }
+      if (this.fields == "hour") {
+        this.dateStr = `${dt.YYYY}-${dt.MM}-${dt.DD} ${dt.hh}`;
+        return;
+      }
+      this.dateStr = `${dt.YYYY}-${dt.MM}-${dt.DD} ${dt.hh}:${dt.mm}`;
+    },
+
+    /**
+     * 设置年数据
+     */
+    setYearData() {
+      // 有效日期
+      let yearStart = this.dtStart.getFullYear();
+      let yearEnd = this.dtEnd.getFullYear();
+      // 年
+      let years = [];
+      for (let year = yearStart; year <= yearEnd; year++) {
+        let item = {
+          number: year,
+          text: `${year}年`
+        };
+        years.push(item);
+      }
+      this.range.splice(0, 1, years);
+    },
+
+    /**
+     * 设置月数据
+     * @param {Number} year 年
+     */
+    setMonthData(year) {
+      // 有效日期
+      let yearStart = this.dtStart.getFullYear();
+      let monthStart = this.dtStart.getMonth() + 1;
+      let yearEnd = this.dtEnd.getFullYear();
+      let monthEnd = this.dtEnd.getMonth() + 1;
+
+      // 月
+      let months = [];
+      let monthStartIndex = year == yearStart ? monthStart : 1;
+      let monthEndIndex = year == yearEnd ? monthEnd : 12;
+      for (let month = monthStartIndex; month <= monthEndIndex; month++) {
+        let item = {
+          number: month,
+          text: `${month}月`
+        };
+        months.push(item);
+      }
+      this.range.splice(1, 1, months);
+    },
+
+    /**
+     * 设置日数据
+     * @param {Number} year 年
+     * @param {Number} month 月
+     */
+    setDayData(year, month) {
+      // 有效日期
+      let yearStart = this.dtStart.getFullYear();
+      let monthStart = this.dtStart.getMonth() + 1;
+      let dayStart = this.dtStart.getDate();
+      let yearEnd = this.dtEnd.getFullYear();
+      let monthEnd = this.dtEnd.getMonth() + 1;
+      let dayEnd = this.dtEnd.getDate();
+
+      // 日
+      let days = [];
+      let dayStartIndex =
+        year == yearStart && month == monthStart ? dayStart : 1;
+      let dayEndIndex;
+      if (year == yearEnd && month == monthEnd) {
+        dayEndIndex = dayEnd;
+      } else {
+        dayEndIndex = new Date(year, month, 0).getDate();
+      }
+      for (let day = dayStartIndex; day <= dayEndIndex; day++) {
+        let item = {
+          number: day,
+          text: `${day}日`
+        };
+        days.push(item);
+      }
+      this.range.splice(2, 1, days);
+    },
+
+    /**
+     * 设置时数据
+     * @param {Number} year 年
+     * @param {Number} month 月
+     * @param {Number} day 日
+     */
+    setHourData(year, month, day) {
+      // 有效日期
+      let yearStart = this.dtStart.getFullYear();
+      let monthStart = this.dtStart.getMonth() + 1;
+      let dayStart = this.dtStart.getDate();
+      let hourStart = this.dtStart.getHours();
+      let yearEnd = this.dtEnd.getFullYear();
+      let monthEnd = this.dtEnd.getMonth() + 1;
+      let dayEnd = this.dtEnd.getDate();
+      let hourEnd = this.dtEnd.getHours();
+
+      // 时
+      let hours = [];
+      let hourStartIndex =
+        year == yearStart && month == monthStart && day == dayStart
+          ? hourStart
+          : 0;
+      let hourEndIndex =
+        year == yearEnd && month == monthEnd && day == dayEnd ? hourEnd : 23;
+      for (let hour = hourStartIndex; hour <= hourEndIndex; hour++) {
+        let item = {
+          number: hour,
+          text: `${hour}时`
+        };
+        hours.push(item);
+      }
+      this.range.splice(3, 1, hours);
+    },
+
+    /**
+     * 设置分数据
+     * @param {Number} year 年
+     * @param {Number} month 月
+     * @param {Number} day 日
+     * @param {Number} hour 时
+     */
+    setMinuteData(year, month, day, hour) {
+      // 有效日期
+      let yearStart = this.dtStart.getFullYear();
+      let monthStart = this.dtStart.getMonth() + 1;
+      let dayStart = this.dtStart.getDate();
+      let hourStart = this.dtStart.getHours();
+      let minuteStart = this.dtStart.getMinutes();
+      let yearEnd = this.dtEnd.getFullYear();
+      let monthEnd = this.dtEnd.getMonth() + 1;
+      let dayEnd = this.dtEnd.getDate();
+      let hourEnd = this.dtEnd.getHours();
+      let minuteEnd = this.dtEnd.getMinutes();
+
+      // 分
+      let minutes = [];
+      let minuteStartIndex =
+        year == yearStart &&
+        month == monthStart &&
+        day == dayStart &&
+        hour == hourStart
+          ? minuteStart
+          : 0;
+      let minuteEndIndex =
+        year == yearEnd && month == monthEnd && day == dayEnd && hour == hourEnd
+          ? minuteEnd
+          : 59;
+      for (let minute = minuteStartIndex; minute <= minuteEndIndex; minute++) {
+        let item = {
+          number: minute,
+          text: `${minute}分`
+        };
+        minutes.push(item);
+      }
+      this.range.splice(4, 1, minutes);
+    },
+
+    /**
+     * 设置默认值
+     */
+    setDefaultValue() {
+      // 默认日期
+      let dtDefault;
+
+      // 开始日期和结束日期
+      let dtStart = this.dtStart;
+      let dtEnd = this.dtEnd;
+
+      // 判断是否传了默认日期
+      // 传了默认日期,格式化默认日期为日期对象
+      if (this.defaultValue) {
+        dtDefault = utils.formatDate(this.defaultValue).dt;
+      }
+      // 如果没有传默认日期,将默认日期设置为当前日期
+      else {
+        dtDefault = new Date();
+      }
+
+      // 如果默认日期不在有效日期范围内,设置默认日期为有效日期开始值
+      if (dtDefault < dtStart || dtDefault > dtEnd) {
+        dtDefault = dtStart;
+      }
+
+      // 更新 dateStr
+      if (this.defaultValue) this.setDateStr(dtDefault);
+
+      // 默认值相关数据
+      let dfYear = dtDefault.getFullYear();
+      let dfMonth = dtDefault.getMonth() + 1;
+      let dfDay = dtDefault.getDate();
+      let dfHour = dtDefault.getHours();
+      let dfMinute = dtDefault.getMinutes();
+
+      // 设置年数据
+      this.setYearData();
+      // 设置 Year 这一列的 value 值
+      let yearIndex = this.range[0].findIndex(year => {
+        return dfYear == year.number;
+      });
+      this.value.splice(0, 1, yearIndex >= 0 ? yearIndex : 0);
+
+      // 设置月数据
+      if (this.fields == "year") return;
+      this.setMonthData(dfYear);
+      // 设置 Month 这一列的 value 值
+      let monthIndex = this.range[1].findIndex(month => {
+        return dfMonth == month.number;
+      });
+      this.value.splice(1, 1, monthIndex >= 0 ? monthIndex : 0);
+
+      // 设置日数据
+      if (this.fields == "month") return;
+      this.setDayData(dfYear, dfMonth);
+      // 设置 Day 这一列的 value 值
+      let dayIndex = this.range[2].findIndex(day => {
+        return dfDay == day.number;
+      });
+      this.value.splice(2, 1, dayIndex >= 0 ? dayIndex : 0);
+
+      // 设置时数据
+      if (this.fields == "day") return;
+      this.setHourData(dfYear, dfMonth, dfDay);
+      // 设置 Hour 这一列的 value 值
+      let hourIndex = this.range[3].findIndex(hour => {
+        return dfHour == hour.number;
+      });
+      this.value.splice(3, 1, hourIndex >= 0 ? hourIndex : 0);
+
+      // 设置分数据
+      if (this.fields == "hour") return;
+      this.setMinuteData(dfYear, dfMonth, dfDay, dfHour);
+      // 设置 Minute 这一列的 value 值
+      let minuteIndex = this.range[4].findIndex(minute => {
+        return dfMinute == minute.number;
+      });
+      this.value.splice(4, 1, minuteIndex >= 0 ? minuteIndex : 0);
+    },
+
+    /**
+     * 某一列的值改变时触发
+     * @param {Number} event.detail.column 表示改变了第几列(下标从0开始)
+     * @param {Number} event.detail.value 表示变更值的下标
+     */
+    columnchange(event) {
+      let columnIndex = event.detail.column; // 改变的列的下标
+      let valueIndex = event.detail.value; // 变更值的下标
+
+      // 更新改变列的 value
+      this.value.splice(columnIndex, 1, valueIndex);
+
+      // 改变年要更新月数据
+      if (this.fields == "year") return;
+      if (columnIndex == 0) {
+        // 当前选择的月
+        let monthBeforeUpdate = this.range[1][this.value[1]];
+        // 更新月数据
+        this.setMonthData(this.range[0][this.value[0]].number);
+        // 更新 Month Value
+        let monthIndex = this.range[1].findIndex(month => {
+          return month.number == monthBeforeUpdate.number;
+        });
+        this.value.splice(1, 1, monthIndex >= 0 ? monthIndex : 0);
+      }
+
+      // 改变年、月都要更新日数据
+      if (this.fields == "month") return;
+      if (columnIndex == 0 || columnIndex == 1) {
+        // 当前选择的日
+        let dayBeforeUpdate = this.range[2][this.value[2]];
+        // 更新日数据
+        this.setDayData(
+          this.range[0][this.value[0]].number,
+          this.range[1][this.value[1]].number
+        );
+        // 更新 Day Value
+        let dayIndex = this.range[2].findIndex(day => {
+          return day.number == dayBeforeUpdate.number;
+        });
+        this.value.splice(2, 1, dayIndex >= 0 ? dayIndex : 0);
+      }
+
+      // 改变年、月、日都要更新时数据
+      if (this.fields == "day") return;
+      if (columnIndex == 0 || columnIndex == 1 || columnIndex == 2) {
+        // 当前选择的时
+        let hourBeforeUpdate = this.range[3][this.value[3]];
+        // 更新时数据
+        this.setHourData(
+          this.range[0][this.value[0]].number,
+          this.range[1][this.value[1]].number,
+          this.range[2][this.value[2]].number
+        );
+        // 更新 Hour Value
+        let hourIndex = this.range[3].findIndex(hour => {
+          return hour.number == hourBeforeUpdate.number;
+        });
+        this.value.splice(3, 1, hourIndex >= 0 ? hourIndex : 0);
+      }
+
+      // 当前选择的分
+      if (this.fields == "hour") return;
+      let minuteBeforeUpdate = this.range[4][this.value[4]];
+      // 更新分数据
+      this.setMinuteData(
+        this.range[0][this.value[0]].number,
+        this.range[1][this.value[1]].number,
+        this.range[2][this.value[2]].number,
+        this.range[3][this.value[3]].number
+      );
+      // 更新 Minute Value
+      let minuteIndex = this.range[4].findIndex(minute => {
+        return minute.number == minuteBeforeUpdate.number;
+      });
+      this.value.splice(4, 1, minuteIndex >= 0 ? minuteIndex : 0);
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.date-time {
+  width: 100%;
+  height: 100%;
+
+  .content {
+    padding: 0 3rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    height: 25rpx;
+  }
+
+  .placeholder {
+    color: #a1a7b3;
+  }
+}
+</style>

+ 343 - 0
components/date-time-picker/utils.js

@@ -0,0 +1,343 @@
+export default {
+	/**
+	 * 同步 try catch 的进一步封装处理
+	 * 使用方法:
+	 * let [err, res] = await this.$utils.asyncTasks(Promise函数);
+	 * if(res) 成功
+	 * if(err) 失败
+	 */
+	asyncTasks(promise) {
+		return promise.then(data => {
+			return [null, data];
+		}).catch(err => [err]);
+	},
+
+	/**
+	 * 精确判断数据是否是 Object 类型
+	 * @param {Any} val 要判断的数据
+	 * @returns {Boolean} true:是;false:不是;
+	 */
+	isObject(val) {
+		return Object.prototype.toString.call(val) === '[object Object]' && val !== null && val !== undefined;
+	},
+
+	/**
+	 * 判断数据是否是 Array 类型
+	 * @param {Any} val 要判断的数据
+	 * @returns {Boolean} true:是;false:不是;
+	 */
+	isArray(val) {
+		return Object.prototype.toString.call(val) === '[object Array]';
+	},
+
+	/**
+	 * 判断数据是否是 String 类型
+	 * @param {Any} val 要判断的数据
+	 * @returns {Boolean} true:是;false:不是;
+	 */
+	isString(val) {
+		return Object.prototype.toString.call(val) === '[object String]';
+	},
+
+	/**
+	 * 精确判断数据是否是 Date 类型
+	 * @param {Any} val 要判断的数据
+	 * @returns {Boolean} true:是;false:不是;
+	 */
+	isDate(val) {
+		return Object.prototype.toString.call(val) === '[object Date]';
+	},
+
+	/**
+	 * 精确判断数据是否是 Function 类型
+	 * @param {Any} val 要判断的数据
+	 * @returns {Boolean} true:是;false:不是;
+	 */
+	isFunction(val) {
+		return Object.prototype.toString.call(val) === '[object Function]';
+	},
+
+	/**
+	 * 精确判断数据是否是 Number 类型
+	 * @param {Any} val 要判断的数据
+	 * @returns {Boolean} true:是;false:不是;
+	 */
+	isNumber(val) {
+		return Object.prototype.toString.call(val) === '[object Number]';
+	},
+
+	/**
+	 * 精确判断数据是否是 Boolean 类型
+	 * @param {Any} val 要判断的数据
+	 * @returns {Boolean} true:是;false:不是;
+	 */
+	isBoolean(val) {
+		return Object.prototype.toString.call(val) === '[object Boolean]';
+	},
+
+	/**
+	 * 判断 URL 是否是绝对 URL。
+	 * @param {String} url 要判断的 URL
+	 * @return {Boolean} true:是绝对URL;false:不是绝对URL;
+	 */
+	isAbsoluteURL(url) {
+		// 如果 URL 以 “<scheme>://” 或 “//”(协议相对URL)开头,则认为它是绝对的
+		// RFC 3986 将方案名称定义为以字母开头的字符序列,然后是字母,数字,加号,句点或连字符的任意组合
+		return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
+	},
+
+	/**
+	 * 合并 baseURL 和相对 URL 成一个完整的 URL
+	 * @param {String} baseURL baseURL
+	 * @param {String} relativeURL 相对 URL
+	 * @returns {String} 返回组合后的完整 URL
+	 */
+	combineURLs(baseURL, relativeURL) {
+		return relativeURL && this.isString(relativeURL) && this.isString(baseURL) ? baseURL.replace(/\/+$/, '') + '/' +
+			relativeURL.replace(/^\/+/, '') : baseURL;
+	},
+
+	/**
+	 * 深度合并对象,只支持合并两个对象,该方法不会改变原有的对象
+	 * @param {Object} FirstOBJ 第一个对象
+	 * @param {Object} SecondOBJ 第二个对象
+	 * @return {Object} 返回深度合并后的对象
+	 */
+	deepMargeObject(FirstOBJ, SecondOBJ) {
+		let ResultOBJ = {};
+		for (let key in FirstOBJ) {
+			ResultOBJ[key] = ResultOBJ[key] && ResultOBJ[key].toString() === "[object Object]" ? this.deepMargeObject(ResultOBJ[
+				key], FirstOBJ[key]) : ResultOBJ[key] = FirstOBJ[key];
+		}
+		for (let key in SecondOBJ) {
+			ResultOBJ[key] = ResultOBJ[key] && ResultOBJ[key].toString() === "[object Object]" ? this.deepMargeObject(ResultOBJ[
+				key], SecondOBJ[key]) : ResultOBJ[key] = SecondOBJ[key];
+		}
+		return ResultOBJ;
+	},
+
+	/**
+	 * 生成指定长度的随机字符串
+	 * @param {Number} min 最小程度
+	 * @param {Number} max 最大长度
+	 * @return {String} 返回生成的字符串
+	 */
+	randomString(min, max) {
+		let returnStr = "",
+			range = (max ? Math.round(Math.random() * (max - min)) + min : min),
+			arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+				'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
+				'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
+			];
+		for (let i = 0; i < range; i++) {
+			let index = Math.round(Math.random() * (arr.length - 1));
+			returnStr += arr[index];
+		}
+		return returnStr;
+	},
+
+	/**
+	 * 格式化日期
+	 * @param {Date|String} date 日期或日期字符串
+	 */
+	formatDate(date) {
+		let YYYY = null;
+		let M = null;
+		let MM = null;
+		let D = null;
+		let DD = null;
+		let h = null;
+		let hh = null;
+		let m = null;
+		let mm = null;
+		let s = null;
+		let ss = null;
+		let ms = null;
+		let ms2 = null;
+		let ms3 = null;
+		let ms4 = null;
+		let dt = null;
+
+		// 如果 date 是 String 类型
+		if (date && this.isString(date)) {
+			// 真机运行时,如果直接用 new Date('YYYY-MM-DD hh:mm:ss') 会报 Invalid Date 错误,所以采用下面的方式创建日期
+			let dtArr = date.replace(/\//g, '.').replace(/-/g, '.').replace(/:/g, '.').replace(/T/g, ' ').replace(' ', '.').replace(
+				'Z', '').split('.');
+
+			let year = 2020;
+			let month = 12;
+			let day = 18;
+			let hour = 0;
+			let minute = 0;
+			let second = 0;
+			let millisecond = 0;
+
+			// 年
+			if (dtArr.length > 0 && !isNaN(dtArr[0])) {
+				year = parseInt(dtArr[0]);
+			}
+			// 月
+			if (dtArr.length > 1 && !isNaN(dtArr[1])) {
+				month = parseInt(dtArr[1]);
+			}
+			// 日
+			if (dtArr.length > 2 && !isNaN(dtArr[2])) {
+				day = parseInt(dtArr[2]);
+			}
+			// 时
+			if (dtArr.length > 3 && !isNaN(dtArr[3])) {
+				hour = parseInt(dtArr[3]);
+			}
+			// 分
+			if (dtArr.length > 4 && !isNaN(dtArr[4])) {
+				minute = parseInt(dtArr[4]);
+			}
+			// 秒
+			if (dtArr.length > 5 && !isNaN(dtArr[5])) {
+				second = parseInt(dtArr[5]);
+			}
+			// 毫秒
+			if (dtArr.length > 6 && !isNaN(dtArr[6])) {
+				millisecond = parseInt(dtArr[6]);
+			}
+
+			date = new Date(year, month - 1, day, hour, minute, second, millisecond);
+		}
+
+		// 如果 date 是 Date 类型
+		if (date && this.isDate(date)) {
+			YYYY = date.getFullYear();
+			M = date.getMonth() + 1;
+			MM = M >= 10 ? M : '0' + M;
+			D = date.getDate();
+			DD = D >= 10 ? D : '0' + D;
+			h = date.getHours();
+			hh = h >= 10 ? h : '0' + h;
+			m = date.getMinutes();
+			mm = m >= 10 ? m : '0' + m;
+			s = date.getSeconds();
+			ss = s >= 10 ? s : '0' + s;
+			ms = date.getMilliseconds();
+			ms2 = ms;
+			ms3 = ms;
+			ms4 = ms;
+			if (ms < 10) {
+				ms2 = '0' + ms;
+				ms3 = '00' + ms;
+				ms4 = '000' + ms;
+			} else if (ms < 100) {
+				ms3 = '0' + ms;
+				ms4 = '00' + ms;
+			} else {
+				ms4 = '0' + ms;
+			}
+		}
+
+		// 返回的数据对象
+		let result = {
+			YYYY: YYYY,
+			MM: MM,
+			M: M,
+			DD: DD,
+			D: D,
+			hh: hh,
+			h: h,
+			mm: mm,
+			m: m,
+			ss: ss,
+			s: s,
+			ms: ms,
+			ms2: ms2,
+			ms3: ms3,
+			ms4: ms4,
+			dt: date,
+			f1: `${YYYY}-${MM}-${DD}`,
+			f2: `${YYYY}年${MM}月${DD}日`,
+			f3: `${YYYY}-${MM}-${DD} ${hh}:${mm}`,
+			f4: `${YYYY}-${MM}-${DD} ${hh}:${mm}:${ss}`,
+			f5:`${YYYY}/${MM}/${DD} ${hh}:${mm}`,
+			f6:`${YYYY}/${MM}/${DD} ${hh}:${mm}:${ss}`,
+			f7: `${hh}:${mm}:${ss}`,
+			f8: `${MM}-${DD}`,
+			f9: `${YYYY}-${MM}`,
+			f10: `${YYYY}年${MM}月`,
+			f11: `${hh}:${mm}`,
+			f12: `${M}月${D}日`,
+			notes: 'YYYY(年),MM(月,补0),M(月,不补0),DD(日,补0),D(日,不补0),hh(时,补0),h(时,不补0),mm(分,补0),m(分,不补0),ss(秒,补0),s(秒,不补0),ms(毫秒,不补0),ms2(毫秒,补0到2位),ms3(毫秒,补0到3位),ms4(毫秒,补0到4位),其余的f1,f2,... 看格式就知道了!'
+		};
+		return result;
+	},
+
+	/**
+	 * 数字转中文
+	 * @param {Number} num 数字
+	 */
+	numberToChinese(num) {
+		if (!/^\d*(\.\d*)?$/.test(num)) return "Number is wrong!";
+		let AA = new Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");
+		let BB = new Array("", "十", "百", "千", "万", "亿", "点", "");
+		let a = ("" + num).replace(/(^0*)/g, "").split("."),
+			k = 0,
+			re = "";
+		for (let i = a[0].length - 1; i >= 0; i--) {
+			switch (k) {
+				case 0:
+					re = BB[7] + re;
+					break;
+				case 4:
+					if (!new RegExp("0{4}\\d{" + (a[0].length - i - 1) + "}$").test(a[0]))
+						re = BB[4] + re;
+					break;
+				case 8:
+					re = BB[5] + re;
+					BB[7] = BB[5];
+					k = 0;
+					break;
+			}
+			if (k % 4 == 2 && a[0].charAt(i + 2) != 0 && a[0].charAt(i + 1) == 0) re = AA[0] + re;
+			if (a[0].charAt(i) != 0) re = AA[a[0].charAt(i)] + BB[k % 4] + re;
+			k++;
+		}
+		if (a.length > 1) //加上小数部分(如果有小数部分)
+		{
+			re += BB[6];
+			for (let i = 0; i < a[1].length; i++) re += AA[a[1].charAt(i)];
+		}
+		return re;
+	},
+
+	/**
+	 * 计算两个经纬度点之间的距离
+	 * @param {Number} lng1 第一个点的经度
+	 * @param {Number} lat1 第一个点的纬度
+	 * @param {Number} lng2 第二个点的经度
+	 * @param {Number} lat2 第二个点的纬度
+	 */
+	calcDistance(lng1, lat1, lng2, lat2) {
+		var radLat1 = lat1 * Math.PI / 180.0;
+		var radLat2 = lat2 * Math.PI / 180.0;
+		var a = radLat1 - radLat2;
+		var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
+		var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
+			Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
+		s = s * 6378.137; // EARTH_RADIUS;
+		s = Math.round(s * 10000) / 10000;
+		return s;
+	},
+
+	/**
+	 * 获取数组最小值的下标
+	 * @param {Array} arr 数组
+	 */
+	getArrayMixValueIndex(arrar) {
+		let min = arrar[0];
+		let index = 0;
+		for (let i = 0; i < arrar.length; i++) {
+			if (min > arrar[i]) {
+				min = arrar[i];
+				index = i;
+			}
+		}
+		return index;
+	}
+}

+ 30 - 0
components/date-time-picker/使用说明.md

@@ -0,0 +1,30 @@
+### 组件说明
+* 日期时间选择器
+* 组件的默认日期有效期范围为:"1970-01-01 00:00" - "2300-01-01 00:00"。
+* 注意:如果您传递的日期有效范围的结束日期小于开始日期,则日期有效范围的结束日期会自动修正为开始日期+300年,比如,您传递的日期有效范围的开始日期为 "2020-11-11 18:30",
+* 结束日期为 "2018-11-11 18:30",则此时将会自动修正结束日期为 "2320-11-11 18:30"。
+* 注意:如果您传递的默认值不在日期有效范围内,则会自动修正默认值为当前日期时间,如果当前日期时间也不在日期有效范围内,则会再次修正为日期有效范围的开始日期。
+* 注意:该组件用到了我自己封装的 utils.js,位置在 '@/common/js/utils.js'。
+
+
+### 插件 props 属性
+* disabled: 是否禁用该组件?Boolean类型;
+* placeholder: 组件没有选中值时显示的内容,String类型;
+* start: 表示有效日期时间范围的开始,String类型,格式为 "YYYY-MM-DD hh:mm";
+* end: 表示有效日期时间范围的结束,String类型,格式必为 "YYYY-MM-DD hh:mm";
+* fields: 选择器的粒度,String类型,有效值为 year、month、day、hour、minute;
+* defaultValue: 默认值,String类型,格式为 "YYYY-MM-DD hh:mm";
+
+### 插件事件
+- change(date):选择日期时间后的回调事件。
+ * date.YYYY: 年;
+ * date.M: 月;
+ * date.MM: 月(补0);
+ * date.D: 日;
+ * date.DD: 日(补0);
+ * date.h: 时;
+ * date.hh: 时(补0);
+ * date.m: 分;
+ * date.mm: 分(补0);
+ * date.dt: Date对象;
+ * ... 还有一些其他的字段,具体看返回值吧!

+ 18 - 0
main.js

@@ -0,0 +1,18 @@
+import Vue from 'vue'
+import App from './App'
+import { store } from './store/index.js';
+import dateTimePickGroup from './components/date-time-pick-group/date-time-pick-group.vue';
+import dateTimePick from './components/date-time-pick/date-time-pick.vue';
+
+Vue.config.productionTip = false;
+Vue.prototype.$store = store;
+Vue.component('com-date-pick-group', dateTimePickGroup);
+Vue.component('com-date-time-pick', dateTimePick);
+
+App.mpType = 'app'
+
+const app = new Vue({
+  ...App,
+  store
+})
+app.$mount()

+ 113 - 0
manifest.json

@@ -0,0 +1,113 @@
+{
+    "name" : "TracerMethodology",
+    "appid" : "__UNI__050063B",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "x86" ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "ad" : {}
+            }
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "h5" : {
+        "title" : "护士端",
+        "router" : {
+            "mode" : "hash",
+            "base" : "/TracerMethodology/"
+        },
+        "devServer" : {
+            "port" : 8901,
+            "headers" : {
+                "Access-Control-Allow-Origin" : "*"
+            },
+            "proxy" : {
+                "/TracerMethodology/api/*" : {
+                    "pathRewrite" : {
+                        "^/TracerMethodology/api" : "/topro"
+                    },
+                    // "target" : "http://192.168.1.55:8901", // 内网
+                    "target" : "http://192.168.1.45:8088", // 内网
+                    "changeOrigin" : true,
+                    "secure" : false,
+                    "logLevel" : "debug"
+                }
+            }
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : true
+            }
+        },
+        "networkTimeout" : {
+            "request" : 5000
+        }
+    }
+}

+ 28 - 0
package-lock.json

@@ -0,0 +1,28 @@
+{
+  "name": "web_nurseside",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "moment": {
+      "version": "2.29.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+      "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+    },
+    "qs": {
+      "version": "6.9.6",
+      "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.9.6.tgz?cache=0&sync_timestamp=1610598122721&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fqs%2Fdownload%2Fqs-6.9.6.tgz",
+      "integrity": "sha1-Ju08gkOkMbKSSsqEzJBHHzXVoO4="
+    },
+    "uview-ui": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npm.taobao.org/uview-ui/download/uview-ui-1.8.3.tgz",
+      "integrity": "sha1-wmAqa/ez7vYD/WMpWes0UK1KC5s="
+    },
+    "vuex": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.0.tgz",
+      "integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ=="
+    }
+  }
+}

+ 22 - 0
package.json

@@ -0,0 +1,22 @@
+{
+  "name": "web_TracerMethodology",
+  "version": "1.0.0",
+  "description": "---\r 1. 公共样式写在App.vue;\r 2. 【pages/index】是公共页,包括左侧信息栏、顶部导航栏、添加按钮,也是所有页面的入口。",
+  "main": "main.js",
+  "dependencies": {
+    "moment": "^2.29.1",
+    "qs": "^6.9.6",
+    "uview-ui": "^1.8.3",
+    "vuex": "^3.6.0"
+  },
+  "devDependencies": {},
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "ssh://yuwenfen@1582597470426922.onaliyun.com@s1.nsloop.com:29418/web_NurseSide.git"
+  },
+  "author": "",
+  "license": "ISC"
+}

+ 27 - 0
pages.json

@@ -0,0 +1,27 @@
+{
+  "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+    {
+      "path": "pages/index/index"
+    }
+  ],
+  "globalStyle": {
+    "navigationBarTitleText": "追踪方法学",
+    "navigationStyle": "custom",
+    "style": {
+      "app-plus": {
+        "background": "#2D43B3"
+      }
+    },
+    "rpxCalcMaxDeviceWidth": 9999
+  },
+  "condition": { //模式配置,仅开发期间生效
+    "current": 0, //当前激活的模式(list 的索引项)
+    "list": [
+      {
+        "name": "开发模式", //模式名称
+        "path": "pages/index/index", //启动页面,必选
+        "query": "" //启动参数,在页面的onLoad函数里面得到
+      }
+    ]
+  }
+}

+ 18 - 0
pages/index/index.vue

@@ -0,0 +1,18 @@
+<template>
+  <view class="index-page">
+		追踪方法学
+  </view>
+</template>
+
+<script>
+
+export default {
+  data() {
+    return {
+    };
+  },
+};
+</script>
+
+<style lang="less">
+</style>

+ 14 - 0
pages/index/model.js

@@ -0,0 +1,14 @@
+import { commServer } from './server.js';
+
+export default {
+  namespaced: true,
+  state: {
+  },
+  mutations: {},
+  actions: {
+		commActions({ commit, state }, { payload }) {
+			// payload = {key,data} // data是请求数据,key是请求接口id
+      return commServer(payload);
+		},
+  }
+}

+ 16 - 0
pages/index/server.js

@@ -0,0 +1,16 @@
+import { creatRequest } from '../../utils/request.js';
+
+const requestList = {
+  //ADL获取接口整合
+  compoundCommon: {
+    method: 'GET',
+    url: 'inpnurse/v1/inpAssessment/compoundCommon',
+		// successMessage: '',
+		// needLoading: false // 还有这两个参数,但不是必传的,默认loading
+  },
+};
+
+export const commServer = ({ key, data }) => {
+  let obj = requestList[key];
+  return creatRequest(obj, data);
+}

+ 5 - 0
pages/model.js

@@ -0,0 +1,5 @@
+import index from './index/model.js';
+
+export default module = {
+  index,
+}

+ 3 - 0
store/actions.js

@@ -0,0 +1,3 @@
+export const actions = {
+	
+}

+ 18 - 0
store/index.js

@@ -0,0 +1,18 @@
+// 使用文档: https://vuex.vuejs.org/zh/
+
+import Vue from 'vue'
+import Vuex from 'vuex'
+import { actions } from './actions'
+import { mutations } from './mutations'
+import module from '../pages/model.js'
+
+Vue.use(Vuex)
+
+export const store = new Vuex.Store({
+	state: { },
+	mutations,
+	actions,
+	modules: {
+		...module
+	}
+})

+ 3 - 0
store/mutations.js

@@ -0,0 +1,3 @@
+export const mutations = {
+	
+}

+ 74 - 0
uni.scss

@@ -0,0 +1,74 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:24rpx;
+$uni-font-size-base:28rpx;
+$uni-font-size-lg:32rpx;
+
+/* 图片尺寸 */
+$uni-img-size-sm:40rpx;
+$uni-img-size-base:52rpx;
+$uni-img-size-lg:80rpx;
+
+/* Border Radius */
+$uni-border-radius-sm: 4rpx;
+$uni-border-radius-base: 6rpx;
+$uni-border-radius-lg: 12rpx;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 10px;
+$uni-spacing-row-base: 20rpx;
+$uni-spacing-row-lg: 30rpx;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 8rpx;
+$uni-spacing-col-base: 16rpx;
+$uni-spacing-col-lg: 24rpx;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:40rpx;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:36rpx;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:30rpx;

+ 150 - 0
utils/request.js

@@ -0,0 +1,150 @@
+/**
+ * 接口请求封装
+ *
+ * 1、需要成功提示的,带 additional.successMessage ,如果值为'msg',提示内容为返回的msg
+ * 2、默认loading,不需要的传 additional.needLoading = false。
+ */
+
+import { URL } from "./requestUrl";
+import { stringify } from 'qs';
+
+const BASE_URL = `${URL}/topro/`;
+let AllRequestNum = 0; // 存放请求数,以保证有请求未返回就持续loading
+
+/**
+ *
+ * @param obj 接口配置数据{url,method,successMessage,needLoading}
+ * @param data 接口请求数据
+ */
+function creatRequest(obj, data) {
+  if (obj) {
+    let additional = {}, url = obj.url;
+    if (obj.successMessage !== undefined) additional.successMessage = obj.successMessage;
+    if (obj.needLoading !== undefined) additional.needLoading = obj.needLoading;
+    // DELETE 参数以问号形式拼接到url
+    if (obj.method === 'DELETE') {
+      url = data ? `${url}?${stringify(data)}` : url;
+      data = null;
+    }
+    return request(url, { method: obj.method, data }, additional);
+  } else {
+    uni.showModal({
+      title: '错误提示',
+      content: '请求的接口未定义!',
+      showCancel: false
+    });
+  }
+}
+
+/**
+ * url: 请求的url
+ * options: uni.request的一系列参数,常用的method和data
+ * additional: { successMessage、needLoading }
+ */
+function request(url, options, additional) {
+  if (additional.needLoading === undefined) additional.needLoading = true;
+  // 加载中提示打开
+  openCloseLoading(additional, true);
+  // 模拟token,做登录的同学将这行代码移到登录成功函数中.
+
+  // uni.setStorageSync('token', 'MjU2OzE2MTE2MjIxODYwNjM7OGJjMTA2ZjNhMzMyNGUxOTRhMmFlMGExOThjMzA5OGE=');
+  const token = uni.getStorageSync('token');
+  return uni.request({
+    url: `${BASE_URL}${url}`,
+    ...options,
+    header: {
+      token: token ? token : null,
+      'Content-Type': 'application/json',
+    }
+  }).then((res) => notifyException(res, additional))
+    .catch((error) => {
+      if ('stack' in error && 'message' in error) {
+        uni.showModal({
+          title: '错误提示',
+          content: '服务器异常,请稍后重试!',
+          showCancel: false
+        });
+        console.error(`请求错误: ${url} ${error.message}`);
+      }
+      return error;
+    });
+}
+
+// 加载中提示打开/关闭
+function openCloseLoading(additional, isOpen) {
+  if (additional.needLoading) {
+    if (isOpen) { // 打开(如果已经打开不必重复打开)
+      if (AllRequestNum === 0)
+        uni.showLoading();
+      AllRequestNum++;
+    } else { // 关闭
+      if (AllRequestNum > 0) { // 防止数值错乱
+        AllRequestNum--;
+        if (AllRequestNum === 0)
+          uni.hideLoading();
+      } else {
+        AllRequestNum = 0;
+      }
+    }
+  }
+}
+
+// 登录过期,重置请求计数,强制关闭loading
+function LoginExpired() {
+  AllRequestNum = 0;
+  uni.navigateTo({
+    url: '/pages/login/login'
+  });
+}
+
+function notifyException(resArr, additional) {
+  openCloseLoading(additional, false);
+  const [error, res] = resArr;
+  if (error) {
+    uni.showModal({
+      title: '错误提示',
+      content: JSON.stringify(error),
+      showCancel: false
+    });
+    console.error(`请求错误: ${error.errMsg}`);
+    return null;
+  } else if (res.data.code !== 'SUCCESS') {
+    const { code, msg } = res.data;
+    if (code === '410' || code === '401') {
+      LoginExpired();
+    } else if (code === '403') {
+      uni.showModal({
+        title: '提示',
+        content: '登录过期,请重新登录!',
+        success: function(_res) {
+          if (_res.confirm) {
+            LoginExpired();
+          } else if (_res.cancel) {
+            console.log('用户点击取消');
+          }
+        }
+      });
+    } else {
+      uni.showModal({
+        title: '错误提示',
+        content: msg,
+        showCancel: false
+      });
+    }
+    console.error(`错误信息: ${msg}`);
+    return null;
+  } else if (additional.successMessage) {
+    let msg = additional.successMessage;
+    uni.showModal({
+      title: '提示',
+      content: msg === 'msg' ? res.data.msg : msg,
+      showCancel: false
+    });
+  }
+  return res.data.data;
+}
+
+export {
+  request,
+  creatRequest
+}

+ 6 - 0
utils/requestUrl.js

@@ -0,0 +1,6 @@
+// export const URL = 'http://192.168.38.140:8088'; // 李磊
+// export const URL = 'http://192.9.216.251:8088'; // 建德
+// export const URL = 'http://192.168.38.174:8088';
+export const URL = 'http://192.168.1.45:8088'; 
+
+