Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

794 linhas
20 KiB

  1. <template>
  2. <view id="home" class="page">
  3. <!-- 顶部搜索栏 -->
  4. <view class="header text-white">
  5. <!-- #ifndef H5 -->
  6. <l-icon
  7. @click="scanClick"
  8. type="scan"
  9. color="white"
  10. class="header-left text-xxl margin-left-sm"
  11. />
  12. <!-- #endif -->
  13. <view @click="moreClick(1)" class="search header-mid margin-lr-sm">
  14. <l-icon type="search" color="white" class="margin-lr-sm" />
  15. 搜索更多应用
  16. </view>
  17. <!-- <l-icon
  18. @click="msgClick"
  19. type="mail"
  20. color="white"
  21. class="header-right text-xxl margin-right-sm"
  22. /> -->
  23. </view>
  24. <!-- 轮播图片 -->
  25. <swiper v-if="imgCount > 0" style="height: 120px">
  26. <swiper-item
  27. v-for="(item, index) of imgData"
  28. :key="index"
  29. style="height: 120px"
  30. >
  31. <image
  32. :src="item"
  33. mode="aspectFill"
  34. style="height: 100%; width: 100%"
  35. ></image>
  36. </swiper-item>
  37. </swiper>
  38. <!-- 功能宫格列表 -->
  39. <view class="col-4 function-list cu-list grid no-border">
  40. <view
  41. v-for="(item, index) in funcListDisplay"
  42. @click="funcListClick(item)"
  43. :key="index"
  44. class="cu-item text-center flex flex-wrap justify-center align-center"
  45. >
  46. <view
  47. class="app-item align-center flex flex-wrap justify-center align-center"
  48. style="position: relative;"
  49. >
  50. <l-icon :type="item.icon" color="white" class="text-sl" />
  51. <view v-if="item.F_Name == '我的审批'&&CornerMarker" class="CornerMarker">
  52. {{CornerMarker}}
  53. </view>
  54. </view>
  55. <text>{{ item.F_Name }}</text>
  56. </view>
  57. </view>
  58. <view class="text-center bg-white margin-bottom"
  59. ><text
  60. @click="moreClick(0)"
  61. class="function-more-btn margin-tb-sm text-gray"
  62. >更多应用</text
  63. ></view
  64. >
  65. <!-- 统计数据宫格 -->
  66. <l-title>统计数据</l-title>
  67. <view class="count-list cu-list grid col-3 margin-bottom">
  68. <view
  69. v-for="(item, index) in countData"
  70. :key="index"
  71. class="cu-item text-center"
  72. >
  73. <text class="margin-bottom-xs">{{ item.title || "(未命名)" }}</text>
  74. <text class="count-item-value">{{ item.value || "-" }}</text>
  75. </view>
  76. </view>
  77. <!-- 通知列表区块 -->
  78. <view
  79. v-for="(block, blockIndex) of noticeData"
  80. :key="blockIndex"
  81. class="margin-bottom"
  82. >
  83. <!-- <view class="margin-top"></view> -->
  84. <l-title :class="{hasmoreBtn:block.title == '通知公告'}" @click="informClick(block.title)">
  85. {{ block.title }}
  86. <l-icon
  87. v-if="block.title == '通知公告'"
  88. type="more"
  89. color="black"
  90. class="moreBtn header-left text-xxl margin-left"
  91. />
  92. </l-title>
  93. <l-list>
  94. <l-list-item
  95. v-for="(item, i) of block.content"
  96. @click="noticeClick(item)"
  97. :key="i"
  98. arrow
  99. >
  100. {{ item.f_title || "(无标题)" }}
  101. <l-tag slot="action" line="gray">{{
  102. postDateTime(item.f_time)
  103. }}</l-tag>
  104. </l-list-item>
  105. </l-list>
  106. </view>
  107. <!-- 图表区块 -->
  108. <view v-for="item of chartData" :key="item.id" class="margin-bottom">
  109. <l-title>{{ item.title }}</l-title>
  110. <view
  111. :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  112. class="chart-list"
  113. >
  114. <!--#ifndef MP-ALIPAY -->
  115. <canvas
  116. v-if="item.type === 1"
  117. @tap="chartTap(item, $event)"
  118. @touchstart="touchStart(item.id, $event)"
  119. @touchmove="touchMove(item.id, $event)"
  120. @touchend="touchEnd(item.id, $event)"
  121. :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  122. :canvas-id="item.id"
  123. :id="item.id"
  124. class="charts"
  125. ></canvas>
  126. <canvas
  127. v-else
  128. @tap="chartTap(item, $event)"
  129. :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  130. :canvas-id="item.id"
  131. :id="item.id"
  132. class="charts"
  133. ></canvas>
  134. <!--#endif -->
  135. <!-- 阿里小程序,需要以2倍尺寸显示,然后缩放为50% -->
  136. <!--#ifdef MP-ALIPAY -->
  137. <canvas
  138. v-if="item.type === 1"
  139. @tap="chartTap(item, $event)"
  140. @touchstart="touchStart(item.id, $event)"
  141. @touchmove="touchMove(item.id, $event)"
  142. @touchend="touchEnd(item.id, $event)"
  143. :style="{
  144. width: cWidth * pixelRatio + 'px',
  145. height: cHeight * pixelRatio + 'px',
  146. transform: 'scale(' + 1 / pixelRatio + ')',
  147. marginLeft: (-cWidth * (pixelRatio - 1)) / 2 + 'px',
  148. marginTop: (-cHeight * (pixelRatio - 1)) / 2 + 'px',
  149. }"
  150. :canvas-id="item.id"
  151. :id="item.id"
  152. class="charts"
  153. ></canvas>
  154. <canvas
  155. v-else
  156. @tap="chartTap(item, $event)"
  157. :style="{
  158. width: cWidth * pixelRatio + 'px',
  159. height: cHeight * pixelRatio + 'px',
  160. transform: 'scale(' + 1 / pixelRatio + ')',
  161. marginLeft: (-cWidth * (pixelRatio - 1)) / 2 + 'px',
  162. marginTop: (-cHeight * (pixelRatio - 1)) / 2 + 'px',
  163. }"
  164. :canvas-id="item.id"
  165. :id="item.id"
  166. class="charts"
  167. ></canvas>
  168. <!--#endif -->
  169. </view>
  170. </view>
  171. </view>
  172. </template>
  173. <script>
  174. import moment from "moment";
  175. import mapValues from "lodash/mapValues";
  176. import uCharts from "@/common/u-charts.js";
  177. // 用于保存图表操作对象
  178. let chartsObject = {};
  179. let chartsConfig = {};
  180. export default {
  181. data() {
  182. return {
  183. imgData: [],
  184. listData: [],
  185. myList: [],
  186. countData: [],
  187. noticeData: [],
  188. chartData: [],
  189. pixelRatio: 1,
  190. cWidth: "",
  191. cHeight: "",
  192. chartsFontSize: 10,
  193. ready:false,
  194. CornerMarker:''
  195. };
  196. },
  197. async onLoad(param) {
  198. await this.init(param);
  199. },
  200. async onShow() {
  201. if(this.ready){
  202. this.ready = false
  203. await this.refresh()
  204. this.ready = true
  205. }
  206. },
  207. // 本页面开启下拉刷新,用于刷新首页数据
  208. onPullDownRefresh() {
  209. this.refresh().then(() => {
  210. this.TOAST("已更新首页数据");
  211. uni.stopPullDownRefresh();
  212. });
  213. },
  214. methods: {
  215. // 页面初始化
  216. async init(param) {
  217. this.HIDE_LOADING();
  218. // 有参数表示可能是打开分享消息;将数据存入全局变量以备后续跳转
  219. if (param && param.learun && param.pagePath) {
  220. this.SET_GLOBAL("jumpParam", param);
  221. }
  222. // 登录状态无效,则跳转到登录页
  223. const stateValid = await this.checkLoginState();
  224. if (!stateValid) {
  225. this.RELAUNCH_TO("/pages/login");
  226. return;
  227. }
  228. // 图表相关参数初始化
  229. this.initCharts();
  230. // 加载页面数据和全局数据
  231. await this.FETCH_CLIENTDATA();
  232. await this.refresh();
  233. this.ready = true
  234. // 监听「我的应用」列表修改
  235. this.ON("home-list-change", () => {
  236. this.HTTP_GET("learun/adms/function/mylist").then((newList) => {
  237. this.myList = newList;
  238. });
  239. });
  240. this.SET_STORAGE("nextTime", null);
  241. // 处理小程序分享消息跳转
  242. // #ifdef MP
  243. const jumpParam = this.GET_GLOBAL("jumpParam");
  244. if (jumpParam) {
  245. this.SET_GLOBAL("jumpParam", null);
  246. this.MP_SHARE_DECODE(jumpParam);
  247. }
  248. // #endif
  249. //微信推送跳转
  250. if (!!param.page) {
  251. //通知公告
  252. if (param.page == "notice") {
  253. this.NAV_TO("/pages/LR_OAModule/list");
  254. }
  255. //邮件
  256. if (param.page == "mail") {
  257. this.NAV_TO("/pages/EducationalAdministration/SYS_ReceiveMessage/list");
  258. }
  259. //OA待办
  260. if (param.page == "oa") {
  261. this.NAV_TO("/pages/nworkflow/myflow/list");
  262. }
  263. //公文下发
  264. if (param.page == "file") {
  265. this.NAV_TO("/pages/EducationalAdministration/Sys_ReceiveFile/list");
  266. }
  267. return;
  268. }
  269. },
  270. // 验证登录状态
  271. async checkLoginState() {
  272. const token = this.GET_GLOBAL("token") || uni.getStorageSync("token");
  273. if (!token || token === "null" || token === "undefined") {
  274. this.RELAUNCH_TO("/pages/login");
  275. this.HIDE_LOADING();
  276. return false;
  277. }
  278. this.SET_GLOBAL("token", token);
  279. // 判断是否有 loginUser 对象
  280. if (this.GET_GLOBAL("loginUser")) {
  281. return true;
  282. }
  283. // 拉取用户信息验证登录态;如果失败则跳转至登录页
  284. const userInfo = await this.HTTP_GET("learun/adms/user/info");
  285. if (!userInfo) {
  286. this.SET_GLOBAL("token", null);
  287. this.SET_STORAGE("token", null);
  288. return false;
  289. }
  290. // 有登录态,则设置用户信息和全局数据
  291. const { baseinfo, mpinfo, post, role } = userInfo;
  292. const user = { ...baseinfo, post, role };
  293. if (mpinfo && Array.isArray(mpinfo) && mpinfo.includes(this.PLATFORM)) {
  294. user.miniProgram = true;
  295. }
  296. this.SET_GLOBAL("loginUser", user);
  297. return true;
  298. },
  299. // 刷新首页数据
  300. async refresh() {
  301. // 清空页面数据
  302. chartsObject = {};
  303. this.imgData = [];
  304. this.listData = [];
  305. this.myList = [];
  306. this.countData = [];
  307. this.noticeData = [];
  308. this.chartData = [];
  309. // 同时发出请求,获取轮播图、所有功能列表、我的功能列表、商机通知提醒图表数据
  310. // 商机、通知提醒图表数据,获取的是数据ID,所以还需要进一步请求
  311. const [imgData, listData, myList, settingData] = await Promise.all([
  312. this.HTTP_GET(
  313. "learun/adms/desktop/imgid" +
  314. (this.CONFIG("isDistributed") == true ? "?isDistributed=true" : "")
  315. ),
  316. this.HTTP_GET("learun/adms/function/list").then(
  317. (result) => result.data
  318. ),
  319. this.HTTP_GET("learun/adms/function/mylist"),
  320. this.HTTP_GET("learun/adms/desktop/setting").then(
  321. (result) => result.data
  322. ),
  323. ]);
  324. this.imgData = imgData;
  325. if (!this.CONFIG("isDistributed"))
  326. // 轮播图需要加上 url 前缀
  327. this.imgData = imgData.map((t) => this.API + `/desktop/img?data=${t}`);
  328. // 功能区按钮需要处理 icon
  329. this.listData = listData.map((item) => {
  330. const icon = item.F_Icon
  331. ? item.F_Icon.replace(`iconfont icon-`, ``)
  332. : "";
  333. const existsIcon = this.getUiIcons().some((t) => t === icon);
  334. return {
  335. ...item,
  336. icon: existsIcon ? icon : "roundright",
  337. };
  338. });
  339. // 我的应用列表需要过滤掉不存在的按钮
  340. this.myList = myList.filter((t) =>
  341. listData.find((item) => item.F_Id === t)
  342. );
  343. // 发出请求,获取商机信息、消息通知信息、图表信息;三类数据全部同时请求
  344. await Promise.all([
  345. ...settingData.target.map((item) =>
  346. this.HTTP_GET("learun/adms/desktop/data", {
  347. type: "Target",
  348. id: item.F_Id,
  349. }).then((data) => {
  350. if (data && data.value) {
  351. const { value } = data;
  352. this.countData.push({
  353. title: item.F_Name,
  354. value,
  355. });
  356. }
  357. })
  358. ),
  359. ...settingData.list.map((item) =>
  360. this.HTTP_GET("learun/adms/desktop/data", {
  361. type: "list",
  362. id: item.F_Id,
  363. }).then((data) => {
  364. if (data && data.value) {
  365. let { value } = data;
  366. if(value&&value.length){
  367. value = value.sort((a,b)=>{
  368. return new Date(b.f_time).valueOf() - new Date(a.f_time).valueOf()
  369. })
  370. }
  371. if(item.F_Name == "待办事项"){
  372. this.CornerMarker = value.length||'0'
  373. }
  374. this.noticeData.push({
  375. title: item.F_Name,
  376. F_Sort:item.F_Sort,
  377. content: value,
  378. });
  379. this.noticeData = this.noticeData.sort((a,b)=>a.F_Sort - b.F_Sort)
  380. }
  381. })
  382. ),
  383. ...settingData.chart.map((item) =>
  384. this.HTTP_GET("learun/adms/desktop/data", {
  385. type: "chart",
  386. id: item.F_Id,
  387. }).then((data) => {
  388. if (data && data.value) {
  389. const { value } = data;
  390. this.chartData.push({
  391. title: item.F_Name,
  392. value,
  393. id: item.F_Id,
  394. type: item.F_Type,
  395. });
  396. }
  397. })
  398. ),
  399. ]);
  400. // 渲染图表
  401. this.chartData.forEach((item) => {
  402. // 根据 item.type 的值选用对应的图表初始化配置项
  403. // 0=环形图;1=折线图;2=柱状图
  404. const charts = new uCharts(
  405. [
  406. {
  407. ...chartsConfig,
  408. canvasId: item.id,
  409. type: "ring",
  410. series: item.value.map((t) => ({
  411. name: t.name,
  412. data: t.value,
  413. })),
  414. extra: {
  415. pie: {
  416. offsetAngle: -45,
  417. ringWidth: 20,
  418. labelWidth: 15,
  419. },
  420. },
  421. legend: {
  422. lineHeight: 20,
  423. },
  424. },
  425. {
  426. ...chartsConfig,
  427. canvasId: item.id,
  428. type: "line",
  429. series: [
  430. {
  431. name: item.title,
  432. data: item.value.map((t) => t.value),
  433. },
  434. ],
  435. categories: item.value.map((t) => t.name),
  436. extra: {
  437. line: {
  438. type: "straight",
  439. },
  440. },
  441. xAxis: {
  442. rotateLabel: true,
  443. fontSize: this.chartsFontSize,
  444. itemCount: 8,
  445. },
  446. enableScroll: true,
  447. },
  448. {
  449. ...chartsConfig,
  450. canvasId: item.id,
  451. type: "column",
  452. series: [
  453. {
  454. name: item.title,
  455. data: item.value.map((t) => t.value),
  456. },
  457. ],
  458. categories: item.value.map((t) => t.name),
  459. xAxis: {
  460. rotateLabel: true,
  461. fontSize: this.chartsFontSize,
  462. },
  463. },
  464. ][item.type]
  465. );
  466. chartsObject[item.id] = charts;
  467. });
  468. },
  469. // 初始化图表
  470. initCharts() {
  471. // (阿里小程序)设置图表尺寸为2倍
  472. // #ifdef MP-ALIPAY
  473. const pixelRatio = uni.getSystemInfoSync().pixelRatio;
  474. if (pixelRatio > 1) {
  475. this.pixelRatio = 2;
  476. this.chartsFontSize = 15;
  477. }
  478. // #endif
  479. this.cWidth = uni.upx2px(750);
  480. this.cHeight = uni.upx2px(500);
  481. chartsConfig = {
  482. $this: this,
  483. pixelRatio: this.pixelRatio,
  484. width: this.cWidth * this.pixelRatio,
  485. height: this.cHeight * this.pixelRatio,
  486. background: "#FFFFFF",
  487. dataLabel: true,
  488. padding: [20, 15, 5, 15],
  489. };
  490. },
  491. // 图表点击事件
  492. chartTap(chartItem, e) {
  493. const format = ({ name, data }, category) =>
  494. `${category || ""} ${name}: ${data}`;
  495. // #ifdef MP-DINGTALK
  496. const detail = e.detail;
  497. const item = detail;
  498. e.mp.changedTouches = [item];
  499. // #endif
  500. chartsObject[chartItem.id].showToolTip(e, {
  501. format,
  502. });
  503. },
  504. // 图表拖动事件(开始)
  505. touchStart(chartId, e) {
  506. chartsObject[chartId].scrollStart(e);
  507. },
  508. // 图表拖动事件(拖动)
  509. touchMove(chartId, e) {
  510. chartsObject[chartId].scroll(e);
  511. },
  512. // 图表拖动事件(结束)
  513. touchEnd(chartId, e) {
  514. chartsObject[chartId].scrollEnd(e);
  515. },
  516. // 格式化日期的显示(新闻列表、通知公告等)
  517. postDateTime(timeStr) {
  518. return moment(timeStr).fromNow();
  519. },
  520. // 点击功能按钮
  521. funcListClick(item) {
  522. if (item.F_IsSystem === 2) {
  523. this.NAV_TO(
  524. `/pages/customapp/list?formId=${item.F_FormId}`,
  525. item,
  526. true
  527. );
  528. return;
  529. }
  530. this.NAV_TO(`/pages/${item.F_Url}/list`);
  531. },
  532. // 点击通知公告的标题
  533. async noticeClick(item) {
  534. if(item.f_category!==undefined){
  535. this.NAV_TO("/pages/home/notice", item, true);
  536. }else{
  537. // const result = await this.HTTP_GET(
  538. // 'learun/adms/newwf/mytask',
  539. // {
  540. // pagination:{"rows":100,"page":1,"sidx":"F_CreateDate","sord":"DESC"},
  541. // queryJson: JSON.stringify({keyword:item.f_title})
  542. // },
  543. // '加载任务时出错'
  544. // )
  545. // if(!result){
  546. // return
  547. // }
  548. // this.processId = this.currentTask.F_Id
  549. // this.taskId = this.currentTask.F_TaskId
  550. let params = {
  551. F_Id:item.f_processid,
  552. F_TaskId:item.f_id,
  553. mark:"pre",
  554. F_Title:item.f_title,
  555. }
  556. this.NAV_TO('/pages/nworkflow/myflow/single', params, true)
  557. }
  558. },
  559. informClick(titleName) {
  560. if (titleName == "通知公告") {
  561. this.NAV_TO("/pages/LR_OAModule/list", "", true);
  562. }
  563. },
  564. // #ifndef H5
  565. // 点击左上角扫码图标,H5 无此功能
  566. scanClick() {
  567. uni.scanCode({
  568. scanType: ["qrCode", "barCode"],
  569. success: ({ result, charSet }) => {
  570. // 您可以在这里自行定制扫码后的功能
  571. },
  572. });
  573. },
  574. // #endif
  575. // 点击更多功能按钮
  576. moreClick(openSearch) {
  577. this.NAV_TO(`/pages/home/more${openSearch ? "?search=1" : ""}`);
  578. },
  579. // 点击右上角的消息按钮
  580. msgClick() {
  581. this.TAB_TO("/pages/msg");
  582. },
  583. },
  584. computed: {
  585. // 「我的功能」区域按钮列表
  586. funcListDisplay() {
  587. const { myList, listData } = this;
  588. const myFuncList = myList.reduce((list, id) => {
  589. if (listData.find((t) => t.F_Id === id)) {
  590. return [...list, listData.find((t) => t.F_Id === id)];
  591. }
  592. return list;
  593. }, []);
  594. return myFuncList;
  595. },
  596. // imgData 数组的长度
  597. imgCount() {
  598. return this.imgData.length;
  599. },
  600. },
  601. };
  602. </script>
  603. <style lang="less" scoped>
  604. .page {
  605. background-color: #f3f3f3;
  606. .header {
  607. height: 100rpx;
  608. background-color: #0c86d8;
  609. display: flex;
  610. align-items: center;
  611. .header-left {
  612. flex-grow: 0;
  613. }
  614. .header-mid {
  615. flex-grow: 1;
  616. background-color: #3d9ee0;
  617. line-height: 60rpx;
  618. height: 60rpx;
  619. font-size: 24rpx;
  620. color: #fff;
  621. border-radius: 6rpx;
  622. }
  623. .header-right {
  624. flex-grow: 0;
  625. }
  626. }
  627. .content {
  628. background-color: #fff;
  629. }
  630. .function-list {
  631. padding-bottom: 0;
  632. .cu-item {
  633. .app-item {
  634. border-radius: 50%;
  635. height: 45px;
  636. width: 45px;
  637. }
  638. &:nth-child(7n + 1) > .app-item {
  639. background-color: #62bbff;
  640. }
  641. &:nth-child(7n + 2) > .app-item {
  642. background-color: #7bd2ff;
  643. }
  644. &:nth-child(7n + 3) > .app-item {
  645. background-color: #ffd761;
  646. }
  647. &:nth-child(7n + 4) > .app-item {
  648. background-color: #fe955c;
  649. }
  650. &:nth-child(7n + 5) > .app-item {
  651. background-color: #ff6283;
  652. }
  653. &:nth-child(7n + 6) > .app-item {
  654. background-color: #60e3f3;
  655. }
  656. &:nth-child(7n) > .app-item {
  657. background-color: #acc8fe;
  658. }
  659. }
  660. }
  661. .function-more-btn {
  662. display: inline-block;
  663. border: currentColor 1px solid;
  664. border-radius: 2px;
  665. padding: 10rpx 50rpx;
  666. }
  667. .count-list {
  668. &:after {
  669. content: "";
  670. clear: both;
  671. display: table;
  672. }
  673. .count-item-value {
  674. color: #0188d2;
  675. font-size: 24px;
  676. }
  677. }
  678. .chart-list {
  679. background-color: #fff;
  680. overflow: hidden;
  681. }
  682. }
  683. </style>
  684. <style lang="less">
  685. #home {
  686. width: 750rpx;
  687. overflow-x: hidden;
  688. .function-list .cu-item text[class*="cuIcon"] {
  689. margin-top: 0 !important;
  690. }
  691. }
  692. .CornerMarker{
  693. padding: 1px 2px;
  694. position: absolute;
  695. right: 0px;
  696. top: 0px;
  697. background-color: red;
  698. min-width: 14px;
  699. border-radius: 7px;
  700. font-size: 12px;
  701. color: #fff;
  702. }
  703. .hasmoreBtn{
  704. .action{
  705. width: 100%;
  706. }
  707. .text-xl{
  708. flex: 1;
  709. position: relative;
  710. }
  711. .moreBtn{
  712. position: absolute;
  713. right: 15px;
  714. }
  715. }
  716. </style>