index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. <template>
  2. <div class="appStore">
  3. <div class="topC">
  4. <img
  5. style="width: 24px;height: 24px;object-fit: contain;"
  6. src="../../assets/images/appStoreCopy/topR.svg"
  7. @click.stop="openCe"
  8. alt=""
  9. />
  10. <img
  11. @click="searchPage"
  12. style="width: 24px;height: 24px;object-fit: contain;"
  13. src="../../assets/images/appStoreCopy/topl.svg"
  14. alt=""
  15. />
  16. </div>
  17. <div id="appStoreCon" class="bodyCon">
  18. <van-popup
  19. round
  20. v-model="CeShow"
  21. :overlay="false"
  22. get-container="#appStoreCon"
  23. position="left"
  24. :overlay-style="{ opacity: 0 }"
  25. :style="{ height: '92vh', transform: 'translate3d(0,0%, 0)', width: '45%', minWidth: '150px', opacity: .95}"
  26. >
  27. <div class="popupCon">
  28. <div class="popTop">
  29. <div style="color: #0663FE;" v-if="userinfo.orgName">【{{ userinfo.orgName }}】</div>
  30. <!-- <div>CocoFlow</div> -->
  31. <div @click="comShow = true">使用电脑端</div>
  32. <!-- <div>关注公众号</div> -->
  33. </div>
  34. <div class="popupBot" @click="goInfo">
  35. <img style="width: 40px;height: 100%;object-fit: contain;color: #000;" src="../../assets/images/appStoreCopy/toux.png" alt="" />
  36. <div style="flex: 1;display: flex;flex-direction: column;gap: 5px;">
  37. <div style="font-size: 16px;font-weight: 600;">{{ userinfo.name }}</div>
  38. <div style="overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{{ userSuffix() }}</div>
  39. </div>
  40. </div>
  41. </div>
  42. </van-popup>
  43. <van-tabs
  44. @change="getData"
  45. @touchmove.stop="handleTouchMove"
  46. background="#F9F8F8"
  47. :line-height="0"
  48. title-active-color="#0663FE"
  49. v-model="tabType"
  50. style="position: relative"
  51. >
  52. <img @click="openSel" style="position: absolute;right: 10px;top: 15px;" src="../../assets/images/appStoreCopy/arrowup.svg" alt="">
  53. <div v-if="selXia" class="xiaH">
  54. <div class="xiaHCon" @click="CutTab(i.id)" v-for="(i,index) in typeList" :key="index">
  55. {{ i.name }}
  56. </div>
  57. </div>
  58. <van-tab v-for="(i, index) in typeList" class="appBlocks" :key="index" :name="i.id" :title="i.name">
  59. <template v-if="dataList.length">
  60. <div v-for="(i, ind) in dataList" @click="openUrl(i.url)" :key="ind + 'a'" class="appBlock">
  61. <div class="appBlockTop">
  62. <img class="appBlockTopImg" :src="i.json.icon" alt="" />
  63. <div class="appBlockTopTit">{{ i.name }}</div>
  64. <div style="display: flex;align-items: center;">
  65. <img src="../../assets/images/appStoreCopy/appde.svg" alt="" />
  66. <div style="display: flex;gap: 5px;margin-left: 8px;">
  67. <span class="ATag">{{ i.label == 'workflow' ? '工作流' :'智能体' }}</span>
  68. <span class="ATag">
  69. <img :src="i.tag == 1 || !i.tag ?
  70. require('../../assets/images/appStoreCopy/dul.svg')
  71. : require('../../assets/images/appStoreCopy/hz.svg')"
  72. alt=""> {{ i.tag == '1' || !i.tag ? '官方' :'精选' }}
  73. </span>
  74. <span
  75. class="ATag"
  76. v-if="
  77. ['agent'].includes(i.label) ||
  78. (['workflow'].includes(i.label) && i.json.modes)
  79. "
  80. >
  81. <template v-if="i.label === 'agent'">聊天式</template>
  82. <template v-else-if="i.json.modes">{{
  83. wayList[i.json.modes[0]]
  84. }}</template>
  85. </span>
  86. </div>
  87. </div>
  88. </div>
  89. <div class="appBlockBot">
  90. <span>{{ i.username }}</span>
  91. <div class="appBlockBotCol">
  92. <div style="display: flex;align-items: center;">
  93. <img @click.stop="delAllOP(i.id,0)" v-if="collList.some(item => item.id === i.id)" src="../../assets/images/appStoreCopy/star1.svg" alt="" />
  94. <img @click.stop="addAllOP(i,0)" v-else src="../../assets/images/appStoreCopy/star.svg" alt="" />
  95. {{ i.collectCount }}
  96. </div>
  97. <div style="display: flex;align-items: center;">
  98. <img @click.stop="delAllOP(i.id,2)" v-if="likeList.some(item => item.id === i.id)" src="../../assets/images/appStoreCopy/xin1.svg" alt="" />
  99. <img @click.stop="addAllOP(i,2)" v-else src="../../assets/images/appStoreCopy/xin.svg" alt="" />
  100. {{ i.likeCount }}
  101. </div>
  102. </div>
  103. </div>
  104. </div>
  105. </template>
  106. <div v-else style="position: absolute;left: 50%;top: 30%;transform: translate(-50%,-50%);">
  107. 暂无数据哦~
  108. </div>
  109. <!-- <div v-if="isShow"
  110. class="zzcl"
  111. >
  112. 加载中...
  113. </div> -->
  114. </van-tab>
  115. </van-tabs>
  116. <div>
  117. </div>
  118. </div>
  119. <!-- 侧边栏遮罩层 -->
  120. <div
  121. v-if="ZeShow"
  122. @click="openCeY"
  123. style="position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: #000;opacity: 0"
  124. ></div>
  125. <van-popup
  126. overlay-class="comCss"
  127. v-model="comShow"
  128. round
  129. :closeable="true"
  130. position="bottom"
  131. class="Vpop"
  132. >
  133. <div class="comCssTit">
  134. 电脑端开启更多高效体验
  135. </div>
  136. <img style="width: 170px;" src="../../assets/images/appStoreCopy/comp.svg" alt="">
  137. <div class="cpmUrl" @click="copyUrl">
  138. <div>
  139. https://cloud.cocorobo.cn/
  140. </div>
  141. <img style="margin-left: 5px;height: 16.8px;" src="../../assets/images/appStoreCopy/copyIco.svg" alt="">
  142. </div>
  143. <div class="comBot">
  144. 复制网址,去电脑端浏览器访问吧
  145. </div>
  146. </van-popup>
  147. </div>
  148. </template>
  149. <script>
  150. import { mapGetters } from 'vuex'
  151. import { getStoreType, getStore, insert_appStoreSave,select_appStoreSave,delete_appStoreSave,select_appStorJuri } from '@/api/appStore'
  152. import appStorePopup from './components/appStorePopup.vue'
  153. import { Dialog, Toast } from 'vant'
  154. const clickOutside = {
  155. bind(el, binding) {
  156. // 在元素上绑定一个点击事件监听器
  157. el.clickOutsideEvent = function(event) {
  158. console.log('event', event)
  159. console.log('村上春树')
  160. // 检查点击事件是否发生在元素的内部
  161. if (!(el === event.target || el.contains(event.target))) {
  162. // 如果点击事件发生在元素的外部,则触发指令绑定的方法,将点击的event数据传过去
  163. binding.value(event)
  164. }
  165. }
  166. // 在文档上添加点击事件监听器
  167. document.addEventListener('tap', el.clickOutsideEvent)
  168. },
  169. unbind(el) {
  170. // 在元素上解除点击事件监听器
  171. document.removeEventListener('tap', el.clickOutsideEvent)
  172. }
  173. }
  174. export default {
  175. name: 'appStore',
  176. components: {
  177. appStorePopup
  178. },
  179. directives: {
  180. 'click-outside': clickOutside // 注册自定义指令
  181. },
  182. data() {
  183. return {
  184. tabType: 0,
  185. CeShow: false,
  186. isShow: false, //loading
  187. comShow:false, //电脑端链接
  188. dataList: [], //应用列表
  189. typeList: [], //tab栏
  190. // showType: '',
  191. searchText: '', //查询框文字
  192. selectJuri: 3,
  193. collList:[], //用户收藏
  194. likeList:[], //用户喜欢
  195. tabPage:'',
  196. ZeShow:false,
  197. selXia:false,
  198. // selectList: [
  199. // { index: 1, label: '我的' },
  200. // { index: 2, label: '组织' },
  201. // { index: 3, label: '所有人' }
  202. // ],
  203. wayList: [
  204. '聊天室',
  205. '卡片式',
  206. '沉浸式',
  207. ],
  208. // 权限标签
  209. juriListData:[],
  210. tagList:[],
  211. showCardId: null
  212. }
  213. },
  214. computed: {
  215. ...mapGetters(['userinfo']),
  216. showMenu() {
  217. return data => {
  218. let _result = false
  219. if (data && this.userinfo.userid) {
  220. if (data.userid === this.userinfo.userid || (data.json && data.json.copy === '1')) {
  221. _result = true
  222. }
  223. }
  224. return _result
  225. }
  226. },
  227. userSuffix() {
  228. let yym = "";
  229. return function () {
  230. // this.userinfo.role == 1 && this.userinfo.rrole == 1 && this.userinfo.type == 1 &&
  231. yym = this.userinfo;
  232. let val = yym.accountNumber;
  233. let userName = JSON.parse(JSON.stringify(val));
  234. const regEmail = new RegExp(
  235. "^[A-Za-z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"
  236. );
  237. // // 判断用户输入账户带不带后缀
  238. if (!regEmail.test(userName)) {
  239. console.log("111");
  240. } else {
  241. const parts = userName.split("@");
  242. userName = parts[0];
  243. }
  244. return userName;
  245. };
  246. }
  247. },
  248. methods: {
  249. async copyUrl() {
  250. const url = "https://cloud.cocorobo.cn/";
  251. try {
  252. await navigator.clipboard.writeText(url);
  253. Toast({
  254. message: '链接已复制!',
  255. position: 'top',
  256. });
  257. } catch (err) {
  258. console.error("复制失败:", err);
  259. Toast({
  260. message: '复制失败,请手动复制',
  261. position: 'top',
  262. });
  263. }
  264. },
  265. // 跳转个人信息页
  266. goInfo(){
  267. this.$router.push('/userInfoPage')
  268. },
  269. // 跳转查询页面
  270. searchPage(){
  271. this.$router.push('/searchL')
  272. },
  273. // 阻止触摸移动事件的默认行为
  274. handleTouchMove(event) {
  275. event.preventDefault()
  276. },
  277. // 打开与关闭侧边栏
  278. openCe() {
  279. this.CeShow = !this.CeShow
  280. this.ZeShow = !this.ZeShow
  281. },
  282. // 点击遮罩层关闭侧边栏
  283. openCeY() {
  284. this.ZeShow = false
  285. this.CeShow = false
  286. this.selXia = false
  287. },
  288. // 查询应用数据
  289. getData(val = '') {
  290. this.getAllOP(0)
  291. this.getAllOP(2)
  292. this.tabPage = val
  293. this.isShow = true
  294. let params = {
  295. uid: this.userinfo.userid,
  296. name: this.searchText,
  297. label: '',
  298. type: val,
  299. juri: this.selectJuri,
  300. stand: this.userinfo.orgArea ? this.userinfo.orgArea : this.userinfo.schoolArea,
  301. status:'',
  302. model:'',
  303. exportType:''
  304. }
  305. getStore(params)
  306. .then(res => {
  307. let _data = res[0]
  308. if (_data.length > 0) {
  309. _data.forEach(i => {
  310. if (i.json) {
  311. i.json = JSON.parse(i.json)
  312. }
  313. })
  314. this.dataList = _data
  315. } else {
  316. this.dataList = []
  317. }
  318. this.getAppStoreJuri()
  319. this.isShow = false
  320. })
  321. .catch(err => {
  322. this.isShow = false
  323. this.$toast.error('获取应用数据失败')
  324. })
  325. },
  326. // 添加收藏0 喜欢2
  327. addAllOP(val,typeL){
  328. let params = [
  329. {
  330. uid: this.userinfo.userid,
  331. type: typeL,
  332. aid: val.id,
  333. json: '',
  334. }
  335. ]
  336. insert_appStoreSave(params)
  337. .then(res => {
  338. console.log(res);
  339. this.getData(this.tabPage)
  340. // this.getAllOP(0)
  341. // this.getAllOP(2)
  342. })
  343. .catch(err => {
  344. console.log(err)
  345. this.$message.error('添加失败')
  346. })
  347. },
  348. // 删除收藏0 喜欢2
  349. delAllOP(val,type){
  350. let kD = ''
  351. if (type == 0) {
  352. kD = this.collList.find(e => val === e.id )
  353. } else {
  354. kD = this.likeList.find(e => val === e.id )
  355. }
  356. let params = [
  357. {
  358. sid: kD.sid
  359. }
  360. ]
  361. delete_appStoreSave(params)
  362. .then(res => {
  363. console.log(res);
  364. this.getData(this.tabPage)
  365. // this.getAllOP(0)
  366. // this.getAllOP(2)
  367. })
  368. .catch(err => {
  369. console.log(err)
  370. this.$message.error('操作失败')
  371. })
  372. },
  373. // 获取点赞 与收藏
  374. getAllOP(val){
  375. let params = {
  376. uid: this.userinfo.userid,
  377. type: val,
  378. limit: 0,
  379. }
  380. select_appStoreSave(params)
  381. .then(res => {
  382. if (val == 0) {
  383. // console.log('111');
  384. this.collList = res[0];
  385. }else{
  386. // console.log('222');
  387. this.likeList = res[0];
  388. }
  389. })
  390. .catch(err => {
  391. console.log(err)
  392. this.$toast.error('获取用户点赞应用失败')
  393. })
  394. },
  395. getAllStoreType() {
  396. let params = {
  397. suserid: this.userinfo.userid,
  398. sorg: this.userinfo.org,
  399. soid: this.userinfo.organizeid,
  400. sstand: this.userinfo.orgArea ? this.userinfo.orgArea : this.userinfo.schoolArea,
  401. exportType:''
  402. }
  403. getStoreType(params)
  404. .then(res => {
  405. let _data = res[0]
  406. if (_data.length > 0) {
  407. this.typeList = _data
  408. this.typeList.unshift({ id: '', name: '全部' })
  409. } else {
  410. this.typeList = []
  411. }
  412. })
  413. .catch(err => {
  414. console.log(err)
  415. this.$toast.error('获取应用类型失败')
  416. })
  417. },
  418. openUrl(url) {
  419. window.open(url, '_blank')
  420. },
  421. setDataTag(_list) {
  422. let _result = _list;
  423. _result.forEach((item) => {
  424. if (
  425. this.juriListData.find(
  426. (i) => i.jid === item[i.type] && i.utype === "1"
  427. ) &&
  428. item.juri == "3"
  429. ) {
  430. item.tag = "1"; //官方
  431. } else if (
  432. this.juriListData.find(
  433. (i) => i.jid === item[i.type] && i.utype === "2"
  434. ) &&
  435. item.juri == "3"
  436. ) {
  437. item.tag = "2"; //精选
  438. } else if (item.role === 1 && item.juri == "2") {
  439. item.tag = "3"; //专属
  440. } else if (item.juri === "2") {
  441. item.tag = "4"; //组织
  442. } else if (item.userid === this.userinfo.userid && item.juri === "1") {
  443. item.tag = "5"; //个人
  444. }
  445. });
  446. return _result;
  447. },
  448. openSel(){
  449. this.selXia = true
  450. this.ZeShow = true
  451. },
  452. CutTab(val){
  453. this.tabType = val
  454. this.selXia = false
  455. this.ZeShow = false
  456. this.getData(val)
  457. },
  458. //获取显示标权限
  459. getAppStoreJuri() {
  460. select_appStorJuri({})
  461. .then(res => {
  462. this.juriListData = res[0];
  463. if (this.dataList.length > 0) {
  464. this.dataList = JSON.parse(JSON.stringify(this.setDataTag(this.dataList)));
  465. }
  466. })
  467. .catch(err => {
  468. console.log(err)
  469. })
  470. },
  471. },
  472. mounted() {
  473. this.getData()
  474. this.getAllStoreType()
  475. }
  476. }
  477. </script>
  478. <style lang="scss" scoped>
  479. .appStore {
  480. width: 100vw;
  481. height: 100vh;
  482. box-sizing: border-box;
  483. // padding: 5px;
  484. background-color: #f9f8f8;
  485. display: flex;
  486. flex-direction: column;
  487. overflow: auto;
  488. }
  489. .topC {
  490. display: flex;
  491. justify-content: space-between;
  492. width: 100%;
  493. align-items: center;
  494. height: 80px;
  495. padding: 0px 12px;
  496. box-sizing: border-box;
  497. }
  498. .bodyCon {
  499. height: 100%;
  500. }
  501. :deep .van-popup--left {
  502. top: auto !important;
  503. }
  504. .popupCon {
  505. display: flex;
  506. flex-direction: column;
  507. justify-content: space-between;
  508. padding: 20px 12px 30px;
  509. height: 100%;
  510. box-sizing: border-box;
  511. .popTop {
  512. color: #000;
  513. display: flex;
  514. flex-direction: column;
  515. font-size: 12px;
  516. gap: 15px;
  517. font-family: PingFang SC;
  518. font-weight: 600;
  519. }
  520. .popupBot {
  521. display: flex;
  522. align-items: center;
  523. gap: 10px;
  524. background: #F3F3F3;
  525. border-radius: 8px;
  526. padding: 5px 8px;
  527. }
  528. }
  529. .appBlocks {
  530. display: grid;
  531. position: relative;
  532. grid-template-columns: repeat(2, 1fr);
  533. // grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  534. padding: 10px;
  535. box-sizing: border-box;
  536. gap: 10px;
  537. grid-auto-rows: 150px; /* 统一高度 */
  538. background: #f9f8f8;
  539. overflow: auto;
  540. .appBlock {
  541. min-width: 100px;
  542. height: 100%;
  543. background: #f8f8f8;
  544. border-radius: 10px;
  545. display: flex;
  546. flex-direction: column;
  547. box-shadow: 0px 4px 10px 0px #00000033;
  548. .appBlockTop {
  549. border-radius: 10px;
  550. padding: 10px;
  551. background: #fff;
  552. box-sizing: border-box;
  553. .appBlockTopImg {
  554. width: 30px;
  555. height: 30px;
  556. object-fit: contain;
  557. border-radius: 50%;
  558. padding: 3px;
  559. box-sizing: border-box;
  560. border: 0.5px #d9d9d9 solid;
  561. margin-bottom: 10px;
  562. }
  563. .appBlockTopTit {
  564. font-family: PingFang SC;
  565. font-weight: 600;
  566. font-size: 14px;
  567. line-height: 15px;
  568. margin-bottom: 10px;
  569. overflow: hidden;
  570. white-space: nowrap;
  571. text-overflow: ellipsis;
  572. }
  573. .ATag {
  574. font-family: PingFang SC;
  575. font-weight: 400;
  576. font-size: 10px;
  577. line-height: 16px;
  578. background: #f3f3f3;
  579. padding: 2px;
  580. border-radius: 2px;
  581. box-sizing: border-box;
  582. display: flex;
  583. align-items: center;
  584. img{
  585. margin-right: 2px;
  586. }
  587. }
  588. }
  589. .appBlockBot {
  590. background: #f8f8f8;
  591. display: flex;
  592. padding: 10px;
  593. flex: 1;
  594. align-items: center;
  595. border-radius: 0 0 10px 10px;
  596. justify-content: space-between;
  597. box-sizing: border-box;
  598. .appBlockBotCol {
  599. display: flex;
  600. gap: 8px;
  601. }
  602. }
  603. }
  604. }
  605. // /deep/ .van-tab {
  606. // width: 100px;
  607. // text-align: center;
  608. // }
  609. // /deep/ .van-tabs__line {
  610. // width: 90px;
  611. // height: 2px;
  612. // flex-shrink: 0;
  613. // background: #4a8efc;
  614. // }
  615. /deep/ .van-tab__pane {
  616. height: calc(92vh - 44px);
  617. overflow-y: auto;
  618. overflow-x: hidden;
  619. width: 100%;
  620. }
  621. /deep/ .comCss{
  622. background: rgba(158, 152, 152, 0.4);
  623. backdrop-filter: blur(3px);
  624. }
  625. /deep/ .van-tabs__wrap{
  626. padding-right: 30px !important;
  627. }
  628. .Vpop{
  629. padding: 20px 30px;
  630. box-sizing: border-box;
  631. height: 350px;
  632. display: flex;
  633. flex-direction: column;
  634. align-items: center;
  635. }
  636. .comCssTit{
  637. font-family: PingFang HK;
  638. font-weight: 600;
  639. font-size: 20px;
  640. line-height: 20px;
  641. color: #0663FE;
  642. margin-bottom: 20px;
  643. }
  644. .cpmUrl{
  645. background: #3681FC;
  646. border-radius: 12px;
  647. padding: 10px 20px;
  648. box-sizing: border-box;
  649. display: flex;
  650. justify-content: center;
  651. align-items: center;
  652. color: #fff;
  653. font-family: PingFang HK;
  654. font-weight: 600;
  655. font-size: 12px;
  656. margin: 10px 0 20px;
  657. }
  658. .comBot{
  659. font-family: PingFang HK;
  660. font-weight: 400;
  661. font-size: 14px;
  662. line-height: 20px;
  663. color: #000;
  664. }
  665. .zzcl{
  666. position: absolute;
  667. left: 0%;top: 0%;
  668. background: #fff;
  669. width: 100%;height: 100%;opacity: .6;
  670. display: flex;justify-content: center;line-height: 350px;
  671. font-size: 16px;
  672. color: #000;
  673. }
  674. .xiaH{
  675. position: absolute;left: 0;top: 44px;
  676. z-index: 999;width: 100%;
  677. background-color: rgb(232, 232, 233);
  678. border-radius: 0 0 15px 15px;
  679. box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
  680. padding: 15px;
  681. box-sizing: border-box;
  682. display: flex;
  683. flex-wrap: wrap;
  684. gap: 10px;
  685. min-height: 100px;
  686. .xiaHCon{
  687. // flex-shrink: 0;
  688. width: auto;
  689. height: 30px;
  690. background: #fff;display: flex;
  691. align-items: center;
  692. border-radius: 5px;
  693. padding: 0 10px;
  694. }
  695. }
  696. </style>