index.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. <template>
  2. <div class="year-month-select">
  3. <div class="year-warp" :style="`width: ${this.showMonth ? 95 : 190}px`">
  4. <el-date-picker
  5. v-model="year"
  6. type="year"
  7. :clearable="false"
  8. placeholder="选择年"
  9. size="small"
  10. value-format="yyyy"
  11. :picker-options="datePickerOptions"
  12. style="width: 100% !important;"
  13. >
  14. </el-date-picker>
  15. </div>
  16. <div v-if="showMonth" class="month-wrap">
  17. <div class="month-list">
  18. <div
  19. :class="[{ active: month == item.value }, 'month-list-item']"
  20. v-for="item of monthList"
  21. :key="item.value"
  22. @click="handleMonthClick(item)"
  23. >
  24. {{ item.name }}
  25. </div>
  26. </div>
  27. </div>
  28. </div>
  29. </template>
  30. <script>
  31. import { getVipYears } from "@/api/system/tenant";
  32. import { mapGetters } from "vuex";
  33. export default {
  34. name: "YearMonthSelect",
  35. props: {
  36. value: {
  37. type: String,
  38. default: "",
  39. },
  40. // 是否显示月份选择
  41. showMonth: {
  42. type: Boolean,
  43. default: true
  44. },
  45. // 是否显示全年
  46. showAllYear: {
  47. type: Boolean,
  48. default: true
  49. }
  50. },
  51. data() {
  52. return {
  53. monthList: [],
  54. year: "",
  55. month: "",
  56. };
  57. },
  58. watch: {
  59. value: {
  60. handler(newVal) {
  61. if (newVal) {
  62. let dateStr = newVal.replace(/-/g, '');
  63. this.year = dateStr.substr(0, 4);
  64. this.month = dateStr.substr(4, 2) || "00";
  65. }
  66. },
  67. immediate: true,
  68. },
  69. year(newVal) {
  70. this.$emit("input", this.formatYearMonth(newVal, this.month));
  71. this.$store.commit("SET_PAGEYEAR", newVal.substr(0, 4));
  72. },
  73. },
  74. computed: {
  75. ...mapGetters(['vipYearList', 'pageYear']),
  76. datePickerOptions() {
  77. return {
  78. disabledDate: time => {
  79. const year = new Date(time).getFullYear();
  80. return !this.vipYearList.includes(year);
  81. }
  82. }
  83. }
  84. },
  85. created() {
  86. this.initMonth();
  87. if (this.vipYearList.length) {
  88. this.setDefaultValue(this.vipYearList);
  89. return;
  90. }
  91. this.getVipYearsList((data) => {
  92. this.setDefaultValue(data);
  93. });
  94. },
  95. methods: {
  96. getVipYearsList(callback) {
  97. const loading = this.$loading({
  98. lock: true,
  99. text: '页面加载中...',
  100. spinner: 'el-icon-loading',
  101. background: 'rgba(0, 0, 0, 0.7)'
  102. });
  103. getVipYears().then(({ data }) => {
  104. loading.close();
  105. let list = data.data.map(year => Number(year));
  106. this.$store.commit("SET_VIP_YEAR", list);
  107. callback && callback(list);
  108. }).catch(() => {
  109. loading.close();
  110. });
  111. },
  112. setDefaultValue(data) {
  113. let date = new Date();
  114. let currYear = date.getFullYear();
  115. let currMonth = date.getMonth() + 1;
  116. let currMonthStr = currMonth > 9 ? currMonth.toString() : `0${currMonth}`;
  117. const target = this.findNumberInArray(data, currYear);
  118. let targetYear;
  119. if (target.targetExists) {
  120. targetYear = target.targetValue;
  121. } else {
  122. targetYear = target.minGreaterThanTarget || target.maxLessThanTarget;
  123. }
  124. this.year = this.pageYear || targetYear.toString();
  125. this.month = currMonthStr;
  126. this.$emit("input", this.formatYearMonth(this.year, this.month));
  127. },
  128. initMonth() {
  129. let list = [];
  130. for (let i = 0; i < 12; i++) {
  131. let month = i + 1;
  132. list.push({
  133. name: month + "月",
  134. value: month > 9 ? month.toString() : "0" + month,
  135. });
  136. }
  137. if (this.showAllYear) {
  138. list.push({ name: "全年", value: "00" });
  139. }
  140. this.monthList = list;
  141. },
  142. formatYearMonth(year, month) {
  143. if (month == "00" || !this.showMonth) {
  144. return year;
  145. }
  146. return year + "" + month;
  147. },
  148. handleMonthClick(data) {
  149. if (!this.vipYearList.length) {
  150. this.$message.error("未开通权限!");
  151. return;
  152. }
  153. this.month = data.value;
  154. this.$emit("input", this.formatYearMonth(this.year, this.month));
  155. },
  156. /**
  157. * 在数组中查找目标数字,如果不存在则找最接近的值
  158. *
  159. * @param {Array} arr - 输入数组
  160. * @param {number} target - 目标数字
  161. * @returns {Object} 结果对象
  162. */
  163. findNumberInArray(arr, target) {
  164. // 检查目标是否在数组中
  165. if (arr.includes(target)) {
  166. return {
  167. targetExists: true,
  168. targetValue: target,
  169. message: `${target} 存在于数组中`
  170. };
  171. }
  172. // 过滤出小于和大于目标值的数字
  173. const lessThan = arr.filter(x => x < target);
  174. const greaterThan = arr.filter(x => x > target);
  175. // 找出小于目标的最大值
  176. const maxLess = lessThan.length > 0 ? Math.max(...lessThan) : null;
  177. // 找出大于目标的最小值
  178. const minGreater = greaterThan.length > 0 ? Math.min(...greaterThan) : null;
  179. return {
  180. targetExists: false,
  181. targetValue: target,
  182. maxLessThanTarget: maxLess,
  183. minGreaterThanTarget: minGreater,
  184. message: `${target} 不存在于数组中`
  185. };
  186. }
  187. },
  188. };
  189. </script>
  190. <style lang="scss">
  191. .year-month-select {
  192. display: flex;
  193. align-items: center;
  194. margin: 12px 0;
  195. }
  196. .month-list {
  197. display: flex;
  198. margin-left: 12px;
  199. &-item {
  200. width: 40px;
  201. height: 30px;
  202. line-height: 30px;
  203. text-align: center;
  204. background-color: #fff;
  205. color: #409eff;
  206. border-radius: 4px;
  207. margin-right: 8px;
  208. border: 1px solid #409eff;
  209. cursor: pointer;
  210. font-size: 12px;
  211. &.active {
  212. background-color: #409eff;
  213. color: #fff;
  214. }
  215. // &.primary {
  216. // background-color: #409EFF;
  217. // border: 1px solid #409EFF;
  218. // }
  219. // &.success {
  220. // background-color: #67c23a;
  221. // border-color: #67c23a;
  222. // }
  223. }
  224. }
  225. </style>