KnowledgeGraph.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. <script setup>
  2. import { ref, reactive, computed } from "vue";
  3. import axios from "axios";
  4. let kwVal = ref(""); // 關鍵字
  5. let stageVal = ref(null); // 指定層級
  6. let maxStage = ref(null); // 最大層級
  7. let haveStage = ref(false); // 是否有層級
  8. let searchState = ref(false); // 點擊查詢
  9. let searchLoading = ref(false); // 載入狀態
  10. let showStageBtn = ref(false); // 新增層級按鈕
  11. let regionsVal = ref(null); // 地區
  12. let languageVal = ref(null); // 語言
  13. let searchError = ref(false); // 查詢狀態
  14. // 取得最大層數
  15. async function getStage() {
  16. if (!regionsVal.value || !languageVal.value) {
  17. searchError.value = true;
  18. return;
  19. } else {
  20. searchError.value = false;
  21. }
  22. searchLoading.value = true;
  23. let url = `https://cmm.ai:8083/get_stage3?kw=${kwVal.value}&region=${regionsVal.value}&language=${languageVal.value}`;
  24. try {
  25. let response = await axios.get(url);
  26. console.log("response", response);
  27. console.log(response.data.message.headers);
  28. searchState.value = true;
  29. if (response.data.message.detail === "Failed to generate stage") {
  30. haveStage.value = false;
  31. } else {
  32. maxStage.value = response.data.message;
  33. haveStage.value = true;
  34. // 層級小於五層才顯示新增按鈕
  35. if (response.data.message < 5) {
  36. showStageBtn.value = true;
  37. } else {
  38. showStageBtn.value = false;
  39. }
  40. }
  41. console.log("response.data.message", response.data.message);
  42. searchLoading.value = false;
  43. } catch (error) {
  44. console.error("error", error);
  45. }
  46. }
  47. // let insertError = ref(false);
  48. // let insertRegionsVal = ref(null); // 新增地區
  49. // let insertLanguageVal = ref(null); // 新增語言
  50. // 新增關鍵字
  51. async function insertKw() {
  52. if (!regionsVal.value || !languageVal.value) {
  53. alert("請選擇要新增的地區及語言");
  54. return;
  55. }
  56. let email = localStorage.getItem("email");
  57. let url = `https://cmm.ai:8083/insert_kw3?kw=${kwVal.value}&region=${regionsVal.value}&language=${languageVal.value}&email=${email}`;
  58. try {
  59. let response = await axios.put(url);
  60. console.log("新增關鍵字", response);
  61. if (response.status === 200) {
  62. alert("關鍵字已加入排程");
  63. // 清空地區及語言
  64. // insertRegionsVal.value = null;
  65. // insertLanguageVal.value = null;
  66. // 關閉查詢結果
  67. // searchState.value = false;
  68. }
  69. getKwStateList();
  70. } catch (error) {
  71. console.error("error", error);
  72. }
  73. }
  74. // let insertDialog = ref(false);
  75. // let insertState = ref(false); // 新增層級
  76. let graphContent = ref(""); // html code
  77. // 取得 html
  78. async function getHtml() {
  79. console.log("getHtml");
  80. let url = `https://cmm.ai:8083/get_html3?kw=${kwVal.value}&region=${regionsVal.value}&language=${languageVal.value}&stage=${stageVal.value}`;
  81. try {
  82. let response = await axios.get(url);
  83. graphContent.value = response.data.data;
  84. // 生成 html 並另開分頁
  85. const newWindow = window.open();
  86. if (newWindow) {
  87. newWindow.document.open();
  88. newWindow.document.write(graphContent.value);
  89. newWindow.document.close();
  90. } else {
  91. alert("無法打開新頁面,請允許瀏覽器的彈出窗口。");
  92. }
  93. } catch (error) {
  94. console.error("操作失敗", error);
  95. alert("操作失敗,請重新操作。");
  96. }
  97. }
  98. // 地區
  99. const regionsList = reactive([
  100. { region: "台灣", code: "tw" },
  101. { region: "馬來西亞", code: "my" },
  102. { region: "美國", code: "us" },
  103. { region: "加拿大", code: "ca" },
  104. { region: "英國", code: "gb" },
  105. { region: "中國", code: "cn" },
  106. { region: "日本", code: "jp" },
  107. { region: "韓國", code: "kr" },
  108. { region: "德國", code: "de" },
  109. { region: "法國", code: "fr" },
  110. { region: "義大利", code: "it" },
  111. { region: "西班牙", code: "es" },
  112. { region: "澳大利亞", code: "au" },
  113. { region: "印度", code: "in" },
  114. { region: "巴西", code: "br" },
  115. { region: "俄羅斯", code: "ru" },
  116. { region: "南非", code: "za" },
  117. { region: "新加坡", code: "sg" },
  118. ]);
  119. // 語言
  120. const languageList = [
  121. { language: "繁體中文", code: "zh-TW" },
  122. { language: "英文", code: "en" },
  123. { language: "簡體中文", code: "zh" },
  124. { language: "西班牙語", code: "es" },
  125. { language: "法語", code: "fr" },
  126. { language: "德語", code: "de" },
  127. { language: "意大利語", code: "it" },
  128. { language: "葡萄牙語", code: "pt" },
  129. { language: "俄語", code: "ru" },
  130. { language: "日語", code: "ja" },
  131. { language: "韓語", code: "ko" },
  132. { language: "阿拉伯語", code: "ar" },
  133. { language: "印地語", code: "hi" },
  134. { language: "泰語", code: "th" },
  135. { language: "越南語", code: "vi" },
  136. { language: "土耳其語", code: "tr" },
  137. { language: "希臘語", code: "el" },
  138. { language: "希伯來語", code: "he" },
  139. { language: "波蘭語", code: "pl" },
  140. { language: "荷蘭語", code: "nl" },
  141. { language: "瑞典語", code: "sv" },
  142. { language: "丹麥語", code: "da" },
  143. { language: "挪威語", code: "no" },
  144. { language: "芬蘭語", code: "fi" },
  145. { language: "捷克語", code: "cs" },
  146. { language: "匈牙利語", code: "hu" },
  147. { language: "斯洛伐克語", code: "sk" },
  148. { language: "斯洛文尼亞語", code: "sl" },
  149. { language: "克羅埃西亞語", code: "hr" },
  150. ];
  151. let stateList = reactive([]);
  152. // 取得關鍵字狀態
  153. async function getKwStateList() {
  154. let email = localStorage.getItem("email");
  155. let url = `https://cmm.ai:8083/get_user_kw_state3?email=${email}`;
  156. try {
  157. let response = await axios.get(url);
  158. console.log("response", response.data.message);
  159. let list = response.data.message;
  160. if (list.length) {
  161. stateList.length = 0;
  162. list.map((item) => {
  163. stateList.push(item);
  164. });
  165. console.log("stateList", stateList);
  166. }
  167. } catch (error) {
  168. console.error("error", error);
  169. }
  170. }
  171. getKwStateList();
  172. const headers = [
  173. {
  174. title: "關鍵字",
  175. sortable: true,
  176. key: "kw",
  177. align: "left",
  178. },
  179. {
  180. title: "狀態",
  181. sortable: true,
  182. key: "state",
  183. align: "left",
  184. },
  185. ];
  186. // 計算下拉選單層級數
  187. const filteredStage = computed(() => {
  188. return Array.from({ length: maxStage.value }, (_, i) => (i + 1).toString());
  189. });
  190. // 插入資料
  191. // function insertData(kw) {
  192. // let url = `https://cmm.ai:8084/insert_data3?kw=${kwVal.value}`;
  193. // axios.put(url).then(function (response) {
  194. // const message = response.data.message; // 假設 API 返回一個帶有消息的 JSON
  195. // alert(message);
  196. // });
  197. // }
  198. // // 將所有流程包進來
  199. // async function processData() {
  200. // console.log("processData", kwVal.value, stageVal.value);
  201. // const url = `https://cmm.ai:8084/check_kw_in_data3?kw=${kwVal.value}&stage=${stageVal.value}`;
  202. // try {
  203. // let response = await axios.get(url);
  204. // const message = response.data.message;
  205. // console.log("response", response);
  206. // if (message === "已查詢到關鍵字, 立即為您產圖.") {
  207. // var confirmNextStep = confirm(
  208. // "已查詢到關鍵字,立即為您產圖。" +
  209. // "\n是否要執行下一步操作?(點確定後將另開分頁)"
  210. // );
  211. // if (confirmNextStep) {
  212. // getHtml();
  213. // } else {
  214. // alert("取消操作");
  215. // }
  216. // }
  217. // } catch (error) {
  218. // console.error("操作失敗", error);
  219. // alert("操作失敗,請重新操作。");
  220. // }
  221. // }
  222. </script>
  223. <template>
  224. <v-container fluid class="knowledge-graph">
  225. <v-card class="mt-10 ma-3 pa-sm-5">
  226. <v-card-title primary-title>
  227. <h3 class="card-title mb-3">GEN SEO</h3>
  228. </v-card-title>
  229. <v-card-text class="card-content">
  230. <div class="d-flex flex-column">
  231. <v-text-field
  232. v-model="kwVal"
  233. label="關鍵字"
  234. density="compact"
  235. prepend-inner-icon="search"
  236. variant="solo"
  237. hide-details
  238. class="mr-5 search-field"
  239. ></v-text-field>
  240. <div class="d-flex align-center mt-5">
  241. <v-select
  242. v-model="regionsVal"
  243. label="地區"
  244. density="compact"
  245. :items="regionsList"
  246. item-title="region"
  247. item-value="code"
  248. variant="solo"
  249. hide-details
  250. :rules="[(value) => !!value || '尚未選擇地區']"
  251. ></v-select>
  252. <v-select
  253. v-model="languageVal"
  254. label="語言"
  255. density="compact"
  256. :items="languageList"
  257. item-title="language"
  258. item-value="code"
  259. variant="solo"
  260. hide-details
  261. class="mx-5"
  262. :rules="[(value) => !!value || '尚未選擇語言']"
  263. ></v-select>
  264. <!-- <v-select
  265. v-model="stageVal"
  266. label="層級"
  267. density="compact"
  268. :items="['1', '2', '3', '4', '5']"
  269. variant="solo"
  270. hide-details
  271. class="my-7 my-md-0 mx-md-5"
  272. ></v-select> -->
  273. <button @click="getStage()" class="search-btn">
  274. <v-progress-circular
  275. v-if="searchLoading"
  276. :width="3"
  277. :size="20"
  278. color="white"
  279. class="pb-1"
  280. indeterminate
  281. ></v-progress-circular>
  282. <p v-else>查詢</p>
  283. </button>
  284. </div>
  285. <p v-if="searchError" class="text-error mt-3 ml-3">
  286. 請選擇查詢地區及語言
  287. </p>
  288. </div>
  289. </v-card-text>
  290. </v-card>
  291. <v-card v-if="searchState" class="mt-10 ma-3 pa-sm-5">
  292. <v-card-title primary-title>
  293. <h3 class="card-title mb-3">查詢結果</h3>
  294. </v-card-title>
  295. <v-card-text class="card-content">
  296. <div v-if="haveStage" class="d-flex flex-column">
  297. <span class="d-flex align-center">
  298. <p class="pt-1 mr-5">目前最大層級:{{ maxStage }}</p>
  299. <v-btn
  300. v-if="showStageBtn"
  301. @click="insertKw()"
  302. variant="outlined"
  303. color="primary"
  304. size="small"
  305. >
  306. 新增層級
  307. </v-btn>
  308. <!-- <v-btn
  309. v-if="showStageBtn"
  310. @click="
  311. insertDialog = true;
  312. insertState = true;
  313. "
  314. variant="outlined"
  315. color="primary"
  316. size="small"
  317. >
  318. 新增層級
  319. </v-btn>
  320. <v-dialog v-model="insertDialog" width="auto">
  321. <v-card width="400" title="新增層級">
  322. <v-card-text class="d-flex mt-5">
  323. <v-select
  324. v-model="insertRegionsVal"
  325. label="地區"
  326. density="compact"
  327. :items="regionsList"
  328. item-title="region"
  329. item-value="code"
  330. variant="solo"
  331. hide-details
  332. :rules="[(value) => !!value || '尚未選擇地區']"
  333. ></v-select>
  334. <v-select
  335. v-model="insertLanguageVal"
  336. label="語言"
  337. density="compact"
  338. :items="languageList"
  339. item-title="language"
  340. item-value="code"
  341. variant="solo"
  342. hide-details
  343. class="mx-5"
  344. :rules="[(value) => !!value || '尚未選擇語言']"
  345. ></v-select>
  346. <p v-if="insertError" class="text-error">請選擇地區及語言</p>
  347. <v-btn @click="insertKw()" color="primary" class="mt-1"
  348. >送出</v-btn
  349. >
  350. </v-card-text>
  351. <v-card-actions>
  352. <v-btn class="ms-auto mr-4" @click="insertDialog = false">
  353. 關閉
  354. </v-btn>
  355. </v-card-actions>
  356. </v-card>
  357. </v-dialog> -->
  358. <!-- <button @click="getStage()" class="insert-btn">新增層級</button> -->
  359. </span>
  360. <span class="d-flex align-center mt-7">
  361. <v-select
  362. v-model="stageVal"
  363. label="請選擇層級"
  364. density="compact"
  365. :items="filteredStage"
  366. variant="solo"
  367. hide-details
  368. ></v-select>
  369. <button @click="getHtml()" class="search-btn ml-3">查看結果</button>
  370. </span>
  371. </div>
  372. <div v-else>
  373. <p>未查詢到符合的關鍵字</p>
  374. <v-btn
  375. @click="insertKw()"
  376. variant="outlined"
  377. color="primary"
  378. class="mt-3"
  379. >
  380. 新增關鍵字
  381. </v-btn>
  382. <!-- <p>未查詢到關鍵字,請選擇要搜尋的地區及語言:</p>
  383. <span class="d-flex mt-5">
  384. <v-select
  385. v-model="insertRegionsVal"
  386. label="地區"
  387. density="compact"
  388. :items="regionsList"
  389. item-title="region"
  390. item-value="code"
  391. variant="solo"
  392. hide-details
  393. :rules="[(value) => !!value || '尚未選擇地區']"
  394. ></v-select>
  395. <v-select
  396. v-model="insertLanguageVal"
  397. label="語言"
  398. density="compact"
  399. :items="languageList"
  400. item-title="language"
  401. item-value="code"
  402. variant="solo"
  403. hide-details
  404. class="mx-5"
  405. :rules="[(value) => !!value || '尚未選擇語言']"
  406. ></v-select>
  407. <p v-if="insertError" class="text-error">請選擇地區及語言</p>
  408. <button @click="insertKw()" class="search-btn">送出</button>
  409. </span> -->
  410. </div>
  411. </v-card-text>
  412. </v-card>
  413. <!-- 關鍵字狀態清單 -->
  414. <v-data-table
  415. v-if="stateList.length"
  416. class="pa-5"
  417. :headers="headers"
  418. :items="stateList"
  419. >
  420. <template v-slot:item.state="{ item }">
  421. <span v-if="item.raw.state === '已完成'">
  422. <v-icon icon="check_circle" color="success" />
  423. 已完成
  424. </span>
  425. <span v-else-if="item.raw.state === '等待中'">
  426. <v-icon icon="pending" color="warning" />
  427. 等待中
  428. </span>
  429. <span v-else-if="item.raw.state === '運行中'">
  430. <v-progress-circular
  431. indeterminate
  432. color="info"
  433. style="width: 20px"
  434. ></v-progress-circular>
  435. 運行中
  436. </span>
  437. <!-- <span v-else>
  438. <v-icon icon="warning" color="error" />
  439. 失敗
  440. </span> -->
  441. </template>
  442. </v-data-table>
  443. </v-container>
  444. </template>
  445. <style lang="scss">
  446. .knowledge-graph {
  447. .v-select {
  448. max-width: 10rem;
  449. }
  450. .v-data-table-footer {
  451. margin-top: 1rem;
  452. }
  453. .search-field {
  454. max-width: 21rem;
  455. }
  456. .search-btn {
  457. padding: 13px 30px 10px;
  458. color: #fff;
  459. font-weight: 500;
  460. letter-spacing: 2px;
  461. border-radius: 100px;
  462. background-image: linear-gradient(
  463. -225deg,
  464. rgb(234, 84, 19) 35%,
  465. rgb(178, 69, 146) 100%
  466. );
  467. }
  468. .card-content {
  469. max-width: 800px;
  470. }
  471. .v-text-field .v-field {
  472. border-radius: 100px;
  473. }
  474. .v-input {
  475. @media (max-width: 960px) {
  476. width: 100%;
  477. }
  478. }
  479. }
  480. </style>