topPage.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. <template>
  2. <div>
  3. <div class="top">
  4. <div class="topBlock">
  5. <div class="topTit" style="display: flex;">
  6. <div>欢迎使用</div>
  7. <div style="color: #0354D7;margin: 0 15px;"><span v-if="userinfo.orgName">{{userinfo.orgName}}—</span>{{ userinfo.schoolName }}</div>
  8. <div>AI平台!</div>
  9. </div>
  10. <div class="topDetail">
  11. <!-- 探索AI教育的无限可能,提升教学效率与学习体验 -->
  12. {{ fromL.basics.brief }}
  13. </div>
  14. </div>
  15. <img style="width: 96px;height: 96px;" src="../assets/img/root.png" alt="">
  16. </div>
  17. <!-- 平台应用 -->
  18. <div class="TabList">
  19. <div
  20. @mouseenter="setHovered(index, true)"
  21. @mouseleave="setHovered(index, false)"
  22. style="position: relative;"
  23. @click="openApp(item)"
  24. v-for="(item,index) in appSignL(fromL.admin.index.list)"
  25. :key="index+'1p'">
  26. <div class="TabListCon">
  27. <div v-for="(p,pin) in AppCon(item.url)" :key="pin+'p'">
  28. <img class="imgApp" :src="hovList[index] ? p.hoverIcon : p.platformIcon" alt="">
  29. <div class="TabListName">
  30. {{ p.name }}
  31. </div>
  32. </div>
  33. <div class="TabListBri">
  34. <el-tooltip class="item" effect="light" :content="item.description" placement="bottom">
  35. <span>
  36. {{ item.description }}
  37. </span>
  38. </el-tooltip>
  39. </div>
  40. </div>
  41. </div>
  42. </div>
  43. <!-- 常见应用 -->
  44. <div class="footCon">
  45. <div class="footConLeft">
  46. <img src="../assets/img/dong.png" alt="">
  47. </div>
  48. <div class="footList">
  49. <div class="footListCon" v-for="(item,index) in admincocoFlow" @click="openNewWindow(item)" :key="index+'2p'">
  50. <div class="footListConimg"><img style="margin-bottom: 12px;height: 24px;width: 22px;" :src="JSON.parse(item.json).icon" alt=""></div>
  51. <div class="TabListName">{{ item.name }}</div>
  52. <div class="TabListBri">
  53. <el-tooltip class="item" effect="light" :content="item.detail" placement="bottom">
  54. <span>
  55. {{ item.detail }}
  56. </span>
  57. </el-tooltip>
  58. </div>
  59. <!-- <div class="cha" @click.stop="delApp(item.lid)">
  60. <img style="width: 20px;" src="../assets/img/cha.svg" alt="">
  61. </div> -->
  62. </div>
  63. </div>
  64. <div class="footList2">
  65. <div class="footList2Tit">
  66. 常见应用
  67. </div>
  68. <div v-for="(i,index) in (4 - CocoFlowList.length)" @click="openUsuallyApp"
  69. :key="index+'3p'" class="footListCon2">
  70. <div style="margin-bottom: 8px;font-size: 40px;color: #0354D7;">
  71. <img src="../assets//img/add.svg" alt="">
  72. </div>
  73. </div>
  74. <div class="footListCon" v-for="(item,index) in CocoFlowList" @click="openNewWindow(item)" :key="index+'4p'">
  75. <div class="footListConimg">
  76. <img style="margin-bottom: 12px;height: 24px;width: 22px;" :src="JSON.parse(item.json).icon" alt="">
  77. </div>
  78. <div class="TabListName">{{ item.name }}</div>
  79. <div class="TabListBri">
  80. <el-tooltip class="item" effect="light" :content="item.detail" placement="bottom">
  81. <span>
  82. {{ item.detail }}
  83. </span>
  84. </el-tooltip>
  85. </div>
  86. <div class="cha" @click.stop="delApp(item.lid)">
  87. <img style="width: 20px;" src="../assets/img/cha.svg" alt="">
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. <!-- 常见应用弹框 -->
  93. <el-dialog
  94. title="应用列表"
  95. :visible.sync="dialogVisible"
  96. class="moreDia"
  97. :close-on-click-modal="false"
  98. :modal="false"
  99. width="60%"
  100. :before-close="handleClose">
  101. <div v-loading="loading" style="display: grid;grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));gap: 16px;height: 300px;overflow: auto;">
  102. <div v-for="(item,index) in isAdd(usuallyList)" class="tabCon" @click="addApp(item.id)" :key="index+'6p'" style="min-width: 308px;">
  103. <div class="AppList">
  104. <img class="appImg" :src="JSON.parse(item.json).icon" alt="">
  105. <div class="con">
  106. <div class="tit">{{ item.name }}</div>
  107. <div class="bri">
  108. <el-tooltip class="item" effect="light" :content="item.detail" placement="bottom">
  109. <span>
  110. {{ item.detail }}
  111. </span>
  112. </el-tooltip>
  113. </div>
  114. </div>
  115. <div style="position: absolute;top: 6px;right: 15px;" v-if="tab.includes(item.id)">
  116. </div>
  117. </div>
  118. </div>
  119. </div>
  120. <div style="height: 45px;line-height: 45px;text-align: end;">
  121. <el-button @click="handleClose">取消</el-button>
  122. <el-button @click="addUsuallyApp" type="primary">确认</el-button>
  123. </div>
  124. </el-dialog>
  125. </div>
  126. </template>
  127. <script>
  128. import { mapGetters } from 'vuex';
  129. import store from '../store'
  130. import { API_CONFIG } from "@/common/apiConfig";
  131. export default {
  132. computed: {
  133. ...mapGetters(['userinfo','fromL']),
  134. // 如果hk,com没有图标,默认使用cn的
  135. AppCon(){
  136. return function(c) {
  137. let k = JSON.parse(JSON.stringify(c))
  138. let data = k.filter(e=>{
  139. return e.region == this.userinfo.schoolArea || e.region == this.userinfo.orgArea
  140. })
  141. let data2 = k.filter(e=>{
  142. return e.region == 'cn'
  143. })
  144. // 如果hk,com没有图标,默认使用cn的
  145. if (!data[0].icon){
  146. data[0].icon = data2[0].icon
  147. data[0].activeIcon = data2[0].activeIcon
  148. }
  149. return data
  150. };
  151. },
  152. // 筛选是否为管理员可见,是否被删除
  153. appSignL(){
  154. return function(val){
  155. let data = []
  156. if (this.userinfo.type == 1 && this.userinfo.role == 1) {
  157. val.forEach( e =>{
  158. if (e.menuName || e.status == 0) {
  159. data.push(e)
  160. }
  161. })
  162. } else {
  163. val.forEach( e =>{
  164. if (e.menuName || (e.isAdmin == '0' && e.status == 0)) {
  165. data.push(e)
  166. }
  167. })
  168. }
  169. return data
  170. }
  171. },
  172. // 判断是否被添加过
  173. isAdd(){
  174. return function(val){
  175. const difference = val.filter(item1 =>
  176. !this.CocoFlowList.some(item2 => item1.id === item2.id)
  177. );
  178. return difference
  179. }
  180. }
  181. },
  182. data() {
  183. return {
  184. dialogVisible:false,
  185. //常见ai应用列表(添加弹框)
  186. usuallyList:[],
  187. loading:false,
  188. //选中应用列表
  189. tab:[],
  190. // 管理平台添加常见cocoFlow应用
  191. admincocoFlow:[],
  192. // 用户ai应用数组
  193. CocoFlowList:[],
  194. hovList:[],
  195. }
  196. },
  197. methods: {
  198. setHovered(index, value) {
  199. this.hovList.splice(index,1,value)
  200. },
  201. // 删除应用
  202. delApp(val){
  203. this.$confirm('确定删除吗', '提示', {
  204. confirmButtonText: '确定',
  205. cancelButtonText: '取消',
  206. type: 'warning'
  207. }).then(async () => {
  208. let params = [
  209. {
  210. functionName: API_CONFIG.ajax_del_usuallyApp.functionName,
  211. aid: val,
  212. },
  213. ];
  214. this.$ajax
  215. .post(API_CONFIG.baseUrl, params)
  216. .then(() => {
  217. this.$message.success('删除成功')
  218. this.getData()
  219. })
  220. .catch((err) => {
  221. console.log(err);
  222. this.$message.error("删除失败");
  223. });
  224. }).catch(() => {
  225. // 取消操作
  226. });
  227. },
  228. handleClose(){
  229. this.usuallyList= []
  230. this.tab= []
  231. this.dialogVisible = false
  232. },
  233. // 弹框选择添加应用
  234. addApp(val){
  235. // let data = this.CocoFlowList.filter(e=>{
  236. // return e.id == val
  237. // })
  238. // if (data.length != 0) return this.$message.info('常用列表已添加')
  239. let kpl = [...this.tab,...this.CocoFlowList]
  240. // console.log(kpl);
  241. const index = this.tab.indexOf(val);
  242. if (index !== -1) {
  243. this.tab.splice(index, 1); // 删除第一个匹配项
  244. } else {
  245. if (kpl.length > 3) return this.$message.info('只能添加四个常用应用哦')
  246. this.tab.push(val); // 添加元素到末尾
  247. }
  248. },
  249. // 打开常见应用弹框
  250. openUsuallyApp(){
  251. this.dialogVisible = true
  252. this.loading = true
  253. let params = [
  254. {
  255. functionName: API_CONFIG.ajax_usuallyApp.functionName,
  256. uid: this.userinfo.userid,
  257. cn: this.userinfo.schoolArea ? this.userinfo.schoolArea : this.userinfo.orgArea, //学校id
  258. },
  259. ];
  260. this.$ajax
  261. .post(API_CONFIG.baseUrl, params)
  262. .then((res) => {
  263. this.usuallyList = res.data[0]
  264. this.loading = false
  265. })
  266. .catch((err) => {
  267. console.log(err);
  268. this.loading = false
  269. this.$message.error("获取工具数据失败");
  270. });
  271. },
  272. // 添加常用确定按钮
  273. async addUsuallyApp(){
  274. const uploadYn = async files => {
  275. for (let index = 0; index < files.length; index++) {
  276. await this.XAdd(files[index]);
  277. console.log(index);
  278. }
  279. }
  280. await uploadYn(this.tab);
  281. console.log('完成了');
  282. this.getData()
  283. this.handleClose()
  284. },
  285. // 循环添加用户选择常见应用
  286. XAdd(val){
  287. return new Promise((resolve) => {
  288. let params = [
  289. {
  290. functionName: API_CONFIG.ajax_add_usuallyApp.functionName,
  291. oid:this.userinfo.organizeid,
  292. aid: val,
  293. uid: this.userinfo.userid,
  294. },
  295. ];
  296. this.$ajax
  297. .post(API_CONFIG.baseUrl, params)
  298. .then(() => {
  299. resolve(1)
  300. })
  301. .catch((err) => {
  302. console.log(err);
  303. });
  304. })
  305. },
  306. // 获取已添加cocoFlow应用
  307. getData(){
  308. let params = [
  309. {
  310. functionName: API_CONFIG.ajax_addedUsuallyApp.functionName,
  311. uid: this.userinfo.userid,
  312. },
  313. ];
  314. this.$ajax
  315. .post(API_CONFIG.baseUrl, params)
  316. .then((res) => {
  317. this.CocoFlowList = res.data[0]
  318. })
  319. .catch((err) => {
  320. console.log(err);
  321. this.$message.error("获取工具数据失败");
  322. });
  323. },
  324. getAdmincocoFlow(){
  325. // console.log('getAdmincocoFlow',this.fromL);
  326. // 如果后台预设常用ai工具为0则不执行
  327. if (this.fromL.admin.cocoFlow.length == 0) return
  328. let params = [
  329. {
  330. functionName: API_CONFIG.ajax_AdminApp.functionName,
  331. con: this.fromL.admin.cocoFlow.join(','),
  332. },
  333. ];
  334. this.$ajax
  335. .post(API_CONFIG.baseUrl, params)
  336. .then((res) => {
  337. this.admincocoFlow = res.data[0]
  338. })
  339. .catch((err) => {
  340. console.log(err);
  341. this.$message.error("获取工具数据失败");
  342. });
  343. },
  344. // 打开平台应用
  345. async openApp(val){
  346. // 点击相同应用不刷新
  347. if (this.appSign == val.toolId) return
  348. // 更新标识
  349. await store.commit('user/SET_AppSIGN', val.toolId)
  350. let url = ''
  351. val.url.forEach(e => {
  352. // if (e.region == this.userinfo.schoolArea || e.region == this.userinfo.orgArea) {
  353. if (e.region == this.$region) {
  354. url = e.url
  355. }
  356. });
  357. let _userinfo = this.userinfo, //登录用户信息
  358. { userid: _userid, organizeid: _oid, type: _type, org: _org, role: _role, classid: _classId } = _userinfo; // 解构赋值获取用户信息
  359. const _TscreenType = 1, _SscreenType = 3; // 常量定义
  360. let queryString = ''
  361. if(val.argumentList && val.argumentList.length){
  362. const paramsMap = {
  363. userid: _userid,
  364. org: _org,
  365. oid: _oid,
  366. tType: _type,
  367. role: _role,
  368. classId: _classId,
  369. TscreenType: _TscreenType,
  370. SscreenType: _SscreenType
  371. };
  372. const canshu = val.argumentList
  373. .filter(param => paramsMap[param] !== undefined)
  374. .map(param => `${param}=${paramsMap[param]}`);
  375. queryString = canshu.length ? (url.includes('?') ? '&' : '?') + canshu.join('&') : ''; // 生成查询字符串
  376. }
  377. let _url = url + queryString
  378. console.log('_url',_url);
  379. let kpl = ` <iframe
  380. v-if="appSign && urlAddress"
  381. allow= "camera *; microphone *;display-capture;midi;encrypted-media;"
  382. frameborder="no"
  383. border="0"
  384. style="border:0;width:100%;height:100%;"
  385. src="${_url}"
  386. ref="pageCon"
  387. >
  388. </iframe>`
  389. let pl = {json:kpl ,stateL :true,toolId :val.toolId}
  390. // this.$emit('AddAppJson',pl)
  391. this.$emit('cutUrl',pl)
  392. // document.querySelector('#pageCon').innerHTML = '';
  393. // window.topU.U.MD.D.I.openApplicationWai(val.toolId, url, dom,val.argumentList)
  394. },
  395. // 打开CocoFlow应用
  396. openNewWindow(val) {
  397. console.log(val);
  398. // // 基本用法:打开指定 URL
  399. window.open(val.url, "_blank");
  400. },
  401. },
  402. }
  403. </script>
  404. <style scoped>
  405. /* 顶部区域 */
  406. .topBlock{
  407. display: flex;
  408. flex-direction: column;
  409. justify-content: space-between;
  410. }
  411. .topTit{
  412. font-size: 30px;
  413. height: 100%;
  414. color: #000;
  415. font-weight: 600;
  416. }
  417. .topDetail{
  418. color: #64748B;
  419. margin-top: 16px;
  420. }
  421. /* 平台应用 */
  422. .TabList{
  423. display: grid;
  424. grid-template-columns: repeat(3, 1fr);
  425. gap: 106px; /* 网格间距 */
  426. margin-bottom: 16px;
  427. }
  428. .imgApp{
  429. width: 120px;
  430. height: 140px;
  431. object-fit: contain;
  432. position: absolute;
  433. top: 50%;
  434. transform: translate(0,-50%);
  435. left: -50px;
  436. }
  437. .TabListCon{
  438. width: 170px;
  439. height: 140px;
  440. padding: 24px;
  441. box-sizing: border-box;
  442. background-color: #fff;
  443. border-radius: 10px;
  444. width: 100%;
  445. transition: all 0.3s ease; /* 统一过渡效果 */
  446. /* background-image: radial-gradient(#E6F0FF 1px, transparent 1px); */
  447. background-size: 20px 20px;
  448. cursor: pointer;
  449. box-shadow: 0px 4px 10px 0px #0000000D;
  450. display: flex;
  451. flex-direction: column;
  452. justify-content: flex-end;
  453. align-items: end;
  454. }
  455. .TabListCon:hover{
  456. transform: translate(-5px,-10px); /* 向上位移 */
  457. box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  458. }
  459. .TabListCon:hover TabListBri{
  460. -webkit-line-clamp: 2;
  461. width: 150px;
  462. }
  463. .TabListName{
  464. color: #1f2937;
  465. font-size: 16px;
  466. font-weight: 600;
  467. padding: 5px 0;
  468. margin-bottom: 4px;
  469. -webkit-line-clamp: 2;
  470. display: -webkit-box;
  471. -webkit-box-orient: vertical;
  472. overflow: hidden;
  473. text-overflow: ellipsis;
  474. }
  475. .TabListBri{
  476. color: #4b5563;
  477. font-size: 12px;
  478. -webkit-line-clamp: 1;
  479. display: -webkit-box;
  480. -webkit-box-orient: vertical;
  481. overflow: hidden;
  482. text-overflow: ellipsis;
  483. }
  484. .tabCon{
  485. transition: all 0.3s ease; /* 统一过渡效果 */
  486. border-radius: 10px;
  487. cursor: pointer;
  488. }
  489. /* 常见应用样式 */
  490. .footCon{
  491. display: flex;
  492. justify-content: space-between;
  493. box-sizing: border-box;
  494. border-radius: 10px;
  495. gap: 30px;
  496. margin: 80px 0;
  497. /* background-image: radial-gradient(#E6F0FF 1px, transparent 1px); */
  498. background-size: 20px 20px;
  499. }
  500. /* 图片 */
  501. .footConLeft{
  502. /* width: 360px; */
  503. flex: 1;
  504. box-sizing: border-box;
  505. }
  506. .footConLeft img{
  507. width: 100%;
  508. }
  509. /* 中间后面设置应用 */
  510. .footList{
  511. display: grid;
  512. grid-template-columns: repeat(4, 1fr);
  513. gap: 16px; /* 网格间距 */
  514. flex: 2;
  515. }
  516. .footListCon{
  517. background-color: #fff;
  518. border-radius: 10px;
  519. padding: 16px;
  520. box-sizing: border-box;
  521. transition: all 0.3s ease; /* 统一过渡效果 */
  522. height: 10vw;
  523. width: 10vw;
  524. min-height: 146px;
  525. max-width: 175px;
  526. max-height: 175px;
  527. background-size: 20px 20px;
  528. cursor: pointer;
  529. position: relative;
  530. }
  531. .footListCon:hover{
  532. transform: translate(-5px,-10px); /* 向上位移 */
  533. box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  534. }
  535. .footListConimg{
  536. height: 40%;
  537. }
  538. .cha{
  539. display: none;position: absolute;top: 10px;right: 10px;
  540. }
  541. .footListCon:hover .cha{
  542. display: block;
  543. }
  544. /* 添加常见应用区域 */
  545. .footList2{
  546. display: grid;
  547. grid-template-columns: repeat(2, 1fr);
  548. gap: 16px;
  549. flex: 1;
  550. position: relative;
  551. }
  552. .footList2Tit{
  553. position: absolute;
  554. top: -30px;
  555. left: 0;
  556. font-family: PingFang SC;
  557. color: #000;
  558. }
  559. .footListCon2{
  560. display: flex;
  561. cursor: pointer;
  562. justify-content: center;
  563. flex-direction: column;
  564. align-items: center;
  565. border: 1px dashed #000000;
  566. width: 10vw;
  567. height: 10vw;
  568. min-height: 146px;
  569. box-sizing: border-box;
  570. max-width: 175px;
  571. max-height: 175px;
  572. border-radius: 12px;
  573. border-width: 1px;
  574. background-color: #E5E5E5;
  575. }
  576. /* 弹框 */
  577. .moreDia >>> .el-dialog{
  578. border-radius: 10px;
  579. }
  580. .moreDia >>> .el-dialog__body{
  581. height: 345px;
  582. /* overflow: auto; */
  583. border-top: 1px #e7e7e7 solid;
  584. }
  585. /* 对号 */
  586. .top{
  587. display: flex;
  588. min-height: 144px;
  589. justify-content: space-between;
  590. background-color: #fff;
  591. align-items: center;
  592. padding: 24px;
  593. box-sizing: border-box;
  594. margin-bottom: 32px;
  595. border-radius: 10px;
  596. box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)
  597. }
  598. .AppList{
  599. flex-wrap: wrap;display: flex;
  600. justify-content: space-between;
  601. border: 1px rgb(243 244 246 / var(--tw-border-opacity, 1)) solid;
  602. border-radius: 10px;padding: 16px;box-sizing: border-box;
  603. position: relative;
  604. }
  605. .appImg{
  606. width: 48px;
  607. height: 48px;
  608. border-radius: 50%;
  609. object-fit: cover;
  610. }
  611. .con{
  612. flex: 1;
  613. margin-left: 12px;
  614. display: flex;
  615. flex-direction: column;
  616. justify-content: center;
  617. }
  618. .tit{
  619. color: #1f2937;font-size: 16px;height: 24px;line-height: 24px;
  620. -webkit-line-clamp: 1;
  621. display: -webkit-box;
  622. -webkit-box-orient: vertical;
  623. overflow: hidden;
  624. text-overflow: ellipsis;
  625. }
  626. .bri{
  627. color: #6b7280;font-size: 12px;height: 16px;line-height: 16px;
  628. overflow: hidden;
  629. -webkit-line-clamp: 1;
  630. display: -webkit-box;
  631. -webkit-box-orient: vertical;
  632. overflow: hidden;
  633. text-overflow: ellipsis;
  634. }
  635. </style>