YTViews.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. <script setup lang="ts">
  2. import { ref, reactive, computed, watch } from "vue";
  3. import { required, emailRules } from "@/utils";
  4. import { useMainStore } from "@/stores/main";
  5. import type { YTViewsUserData } from "@/interfaces";
  6. import Navbar from "@/components/Navbar.vue";
  7. const mainStore = useMainStore();
  8. const urlRules = [
  9. (v: any) =>
  10. /^(http|https):\/\//.test(v) || "請輸入以 http 或 https 開頭的有效網址",
  11. ];
  12. const items = reactive([
  13. { title: "100% 真人觀看" },
  14. { title: "包含影片設定費" },
  15. { title: "開發票" },
  16. { title: "包含成效報表" },
  17. ]);
  18. const cardItems = reactive([
  19. { view: "5,000", price: "2,700", originalPrice: "3,500", param: 2700 },
  20. { view: "10,000", price: "4,400", originalPrice: "5,000", param: 4400 },
  21. { view: "30,000", price: "12,400", originalPrice: "13,000", param: 12400 },
  22. { view: "50,000", price: "20,400", originalPrice: "21,000", param: 20400 },
  23. ]);
  24. const ageOptions = [
  25. { label: "18 - 24 歲" },
  26. { label: "25 - 34 歲" },
  27. { label: "35 - 44 歲" },
  28. { label: "45 - 54 歲" },
  29. { label: "55 - 64 歲" },
  30. { label: "65 歲以上" },
  31. ];
  32. const objectOptions = [
  33. { label: "交通工具與運輸" },
  34. { label: "媒體和娛樂" },
  35. { label: "家居與園藝" },
  36. { label: "新聞與政治" },
  37. { label: "旅遊" },
  38. { label: "生活型態與興趣" },
  39. { label: "科技" },
  40. { label: "美容與健康" },
  41. { label: "美食與餐飲" },
  42. { label: "購物愛好者" },
  43. { label: "運動與健身" },
  44. { label: "銀行與金融" },
  45. ];
  46. const themeOptions = [
  47. { label: "人文與社會" },
  48. { label: "保健" },
  49. { label: "全球地點(各地區)" },
  50. { label: "參考資料(圖書館、博物館與目錄、清單等)" },
  51. { label: "圖書與文學" },
  52. { label: "家居與園藝" },
  53. { label: "寵物與動物" },
  54. { label: "工作與教育" },
  55. { label: "工商業" },
  56. { label: "房地產" },
  57. { label: "新聞" },
  58. { label: "旅遊與交通" },
  59. { label: "汽車與交通工具" },
  60. { label: "法律與政府" },
  61. { label: "科學" },
  62. { label: "網路社群" },
  63. { label: "網際網路與電信" },
  64. { label: "美容與健身" },
  65. { label: "美食佳飲" },
  66. { label: "興趣與休閒" },
  67. { label: "藝術與娛樂" },
  68. { label: "購物" },
  69. { label: "遊戲" },
  70. { label: "運動" },
  71. { label: "金融" },
  72. { label: "電腦和電子產品" },
  73. ];
  74. let chooseError = ref(false);
  75. let assignView = ref();
  76. let assignPrice = ref();
  77. function activeBtn(view: string, param: number) {
  78. assignView.value = view;
  79. assignPrice.value = param;
  80. chooseError.value = false;
  81. }
  82. let userData = reactive({
  83. email: "",
  84. name: "",
  85. phone: "",
  86. company: "",
  87. url: "",
  88. area: "",
  89. language: "",
  90. ages: [],
  91. target: "",
  92. theme: "",
  93. taxID: "",
  94. });
  95. // 其他選項
  96. let target = ref([]);
  97. let theme = ref([]);
  98. let otherTarget = ref("");
  99. let otherTheme = ref("");
  100. let checkOtherTarget = ref("");
  101. let checkOtherTheme = ref("");
  102. watch(otherTarget, (newVal) => {
  103. if (newVal !== "") {
  104. checkOtherTarget.value = "其他";
  105. } else {
  106. checkOtherTarget.value = "";
  107. }
  108. });
  109. watch(otherTheme, (newVal) => {
  110. if (newVal !== "") {
  111. checkOtherTheme.value = "其他";
  112. } else {
  113. checkOtherTheme.value = "";
  114. }
  115. });
  116. // 檢查必填欄位
  117. const isSubmitDisabled = computed(() => {
  118. return (
  119. !userData.email ||
  120. !userData.name ||
  121. !userData.phone ||
  122. !userData.url ||
  123. !userData.area ||
  124. !userData.language ||
  125. !target.value ||
  126. !theme.value
  127. );
  128. });
  129. async function ECPaySubmit() {
  130. if (!assignPrice.value) {
  131. chooseError.value = true;
  132. window.scrollTo({ top: 0, behavior: "smooth" });
  133. } else {
  134. chooseError.value = false;
  135. }
  136. if (checkOtherTarget.value === "其他" && otherTarget.value !== "") {
  137. userData.target = `${target.value.join("、")}、${otherTarget.value}`;
  138. } else {
  139. userData.target = target.value.join("、");
  140. }
  141. if (checkOtherTheme.value === "其他" && otherTheme.value !== "") {
  142. userData.theme = `${theme.value.join("、")}、${otherTheme.value}`;
  143. } else {
  144. userData.theme = theme.value.join("、");
  145. }
  146. let lang = ref("");
  147. let getLang = localStorage.getItem("lang");
  148. // 綠界顯示語言
  149. if (!getLang || getLang === "zh") {
  150. lang.value = "ZH";
  151. } else {
  152. lang.value = "ENG";
  153. }
  154. let data: YTViewsUserData = {
  155. item: `YT觀看數(${assignView.value} 次觀看)`,
  156. amount: assignPrice.value,
  157. email: userData.email,
  158. name: userData.name,
  159. phone: userData.phone,
  160. company: userData.company,
  161. url: userData.url,
  162. area: userData.area,
  163. language: userData.language,
  164. ages: userData.ages.join("、"),
  165. target: userData.target,
  166. theme: userData.theme,
  167. taxID: userData.taxID,
  168. };
  169. const originalHTML = await mainStore.YTViewsPayment(data, lang.value);
  170. let formHTML = originalHTML?.replace(
  171. '<script type="text/javascript">document.getElementById("data_set").submit();</scr',
  172. ""
  173. );
  174. formHTML = formHTML?.replace("ipt>", "");
  175. const payFormElement = document.getElementById("pay-form");
  176. payFormElement!.innerHTML = formHTML!;
  177. const ecpayForm: HTMLFormElement = <HTMLFormElement>(
  178. document.getElementById("data_set")
  179. );
  180. console.log(ecpayForm);
  181. ecpayForm.submit();
  182. }
  183. // 資料存進 Google Sheets
  184. // async function saveData() {
  185. // const scriptURL =
  186. // "https://script.google.com/macros/s/AKfycbxCcfiOQ695DaxIa3peClqRRTWNj2aUNLbx7ty8U2wKlyU7wreQLioHG-sls5MPKBdlRQ/exec";
  187. // let formdata = new FormData();
  188. // formdata.append("email", userData.email);
  189. // formdata.append("name", userData.name);
  190. // formdata.append("company", userData.company);
  191. // formdata.append("url", userData.url);
  192. // formdata.append("area", userData.area);
  193. // formdata.append("language", userData.language);
  194. // formdata.append("age", userData.age.join("、"));
  195. // formdata.append("object", userData.object);
  196. // formdata.append("theme", userData.theme);
  197. // formdata.append("tax", userData.tax);
  198. // axios
  199. // .post(scriptURL, formdata)
  200. // .then(function (response) {
  201. // console.log(response.data);
  202. // })
  203. // .catch(function (error) {
  204. // console.log(error);
  205. // });
  206. // }
  207. </script>
  208. <template>
  209. <Navbar />
  210. <v-container fluid class="mt-16">
  211. <v-card class="ma-3 pa-3">
  212. <v-card-title primary-title class="mb-3">
  213. <h3 class="headline primary--text">YouTube 觀看數</h3>
  214. </v-card-title>
  215. <v-card-text>
  216. <p class="ms-3">請選擇方案:</p>
  217. <v-row no-gutters class="pay-card">
  218. <v-col
  219. xs="12"
  220. sm="6"
  221. lg="3"
  222. v-for="(item, index) in cardItems"
  223. :key="index"
  224. >
  225. <button @click="activeBtn(item.view, item.param)" class="w-100">
  226. <v-card
  227. class="ma-3 py-3"
  228. :class="{ active: assignPrice === item.param }"
  229. >
  230. <v-card-title primary-title class="pa-0">
  231. <div class="d-flex flex-column">
  232. <section class="d-flex mx-auto">
  233. <img
  234. width="30"
  235. height="30"
  236. src="@/assets/img/icon/play-button.png"
  237. alt=""
  238. class="me-2"
  239. />
  240. <h5 class="m-0">{{ item.view }}</h5>
  241. </section>
  242. <span class="text-center" style="color: #7c8ba7"
  243. >Views</span
  244. >
  245. </div>
  246. <p class="price">
  247. NT${{ item.price }} <br />
  248. <small>NT${{ item.originalPrice }}</small>
  249. </p>
  250. </v-card-title>
  251. <v-card-text class="d-flex align-center justify-center mt-3">
  252. <ul>
  253. <li
  254. v-for="(item, index) in items"
  255. :key="index"
  256. class="d-flex align-center"
  257. >
  258. <img
  259. width="30"
  260. src="@/assets/img/icon/check.png"
  261. alt=""
  262. />
  263. {{ item.title }}
  264. </li>
  265. </ul>
  266. </v-card-text>
  267. <!-- <v-card-actions class="d-flex justify-center">
  268. <v-btn @click="ECPaySubmit(item.param)"> Buy Now </v-btn>
  269. </v-card-actions> -->
  270. </v-card>
  271. </button>
  272. </v-col>
  273. <p class="ms-3 error" v-show="chooseError">尚未選擇方案</p>
  274. </v-row>
  275. <v-sheet max-width="500" class="mx-auto mt-10">
  276. <v-form @submit.prevent class="ECPay-form">
  277. <v-text-field
  278. v-model="userData.email"
  279. :rules="emailRules()"
  280. label="電子郵件"
  281. required
  282. ></v-text-field>
  283. <v-text-field
  284. v-model="userData.name"
  285. :rules="required()"
  286. label="姓名"
  287. required
  288. ></v-text-field>
  289. <v-text-field
  290. v-model="userData.phone"
  291. :rules="[(v:any) => /^0\d{0,9}$/i.test(v) || '請輸入有效的電話號碼']"
  292. label="電話號碼"
  293. required
  294. ></v-text-field>
  295. <v-text-field
  296. v-model="userData.company"
  297. label="公司 / 所屬產業"
  298. ></v-text-field>
  299. <v-text-field
  300. v-model="userData.url"
  301. :rules="urlRules"
  302. label="YouTube 影片網址"
  303. required
  304. ></v-text-field>
  305. <v-text-field
  306. v-model="userData.area"
  307. :rules="required()"
  308. label="影片放送地區(國家 / 縣市)"
  309. required
  310. ></v-text-field>
  311. <v-text-field
  312. v-model="userData.language"
  313. :rules="required()"
  314. label="受眾語言"
  315. required
  316. ></v-text-field>
  317. <p class="mt-5">客層(未選擇的話視為全部)</p>
  318. <div class="checkbox ms-5">
  319. <v-checkbox
  320. v-for="option in ageOptions"
  321. v-model="userData.ages"
  322. :key="option.label"
  323. :label="option.label"
  324. :value="option.label"
  325. color="primary"
  326. ></v-checkbox>
  327. </div>
  328. <p class="mt-10 mb-3">
  329. 目標對象區隔(興趣、習慣)<span class="text-red-darken-1">*</span>
  330. </p>
  331. <div class="checkbox ms-5">
  332. <v-checkbox
  333. v-for="option in objectOptions"
  334. v-model="target"
  335. :key="option.label"
  336. :label="option.label"
  337. :value="option.label"
  338. color="primary"
  339. ></v-checkbox>
  340. <v-checkbox
  341. v-model="checkOtherTarget"
  342. label="其他"
  343. value="其他"
  344. color="primary"
  345. ></v-checkbox>
  346. <input v-model="otherTarget" type="text" class="other" />
  347. <!-- <v-radio-group v-model="target">
  348. <v-radio
  349. v-for="option in objectOptions"
  350. :key="option.label"
  351. :label="option.label"
  352. :value="option.label"
  353. color="primary"
  354. ></v-radio>
  355. <v-radio label="其他" value="其他" color="primary"></v-radio>
  356. <input v-model="otherTarget" type="text" class="other" />
  357. </v-radio-group> -->
  358. </div>
  359. <p class="mt-5 mb-3">
  360. 影片主題 <span class="text-red-darken-1">*</span>
  361. </p>
  362. <div class="checkbox ms-5 mb-5">
  363. <v-checkbox
  364. v-for="option in themeOptions"
  365. v-model="theme"
  366. :key="option.label"
  367. :label="option.label"
  368. :value="option.label"
  369. color="primary"
  370. ></v-checkbox>
  371. <v-checkbox
  372. v-model="checkOtherTheme"
  373. label="其他"
  374. value="其他"
  375. color="primary"
  376. ></v-checkbox>
  377. <input v-model="otherTheme" type="text" class="other" />
  378. <!-- <v-radio-group v-model="theme">
  379. <v-radio
  380. v-for="option in themeOptions"
  381. :key="option.label"
  382. :label="option.label"
  383. :value="option.label"
  384. color="primary"
  385. ></v-radio>
  386. <v-radio label="其他" value="其他" color="primary"></v-radio>
  387. <input v-model="otherTheme" type="text" class="other" />
  388. </v-radio-group> -->
  389. </div>
  390. <v-text-field
  391. type="number"
  392. label="是否需要統編(可填寫統編號碼)"
  393. v-model="userData.taxID"
  394. ></v-text-field>
  395. <v-btn
  396. @click="ECPaySubmit()"
  397. type="submit"
  398. block
  399. class="mt-2 submit-btn"
  400. :disabled="isSubmitDisabled"
  401. >送出</v-btn
  402. >
  403. </v-form>
  404. </v-sheet>
  405. </v-card-text>
  406. </v-card>
  407. </v-container>
  408. <div id="pay-form"></div>
  409. </template>
  410. <style lang="scss">
  411. .pay-card {
  412. .v-card-title {
  413. h5 {
  414. font-size: 20px;
  415. }
  416. span {
  417. font-size: 16px;
  418. letter-spacing: 1px;
  419. }
  420. .price {
  421. padding: 10px 0;
  422. font-size: 26px;
  423. font-weight: 600;
  424. text-align: center;
  425. color: #fff;
  426. background-color: var(--main-color);
  427. letter-spacing: 2px;
  428. small {
  429. display: block;
  430. margin-top: -3px;
  431. font-size: 18px;
  432. font-weight: 100;
  433. text-decoration: line-through;
  434. }
  435. }
  436. }
  437. .v-card-text {
  438. ul {
  439. padding: 0;
  440. list-style: none;
  441. }
  442. }
  443. .v-card-actions {
  444. button {
  445. padding: 5px 15px;
  446. color: #fff;
  447. border-radius: 100px;
  448. background-color: var(--main-color);
  449. border: 1px solid transparent;
  450. &:hover {
  451. color: var(--main-color);
  452. background-color: #fff;
  453. border: 1px solid var(--main-color);
  454. }
  455. }
  456. }
  457. .active {
  458. border: 3px solid var(--main-color);
  459. }
  460. .error {
  461. color: #b00020;
  462. }
  463. }
  464. .ECPay-form {
  465. font-size: 16px;
  466. .checkbox {
  467. margin-left: 5px;
  468. list-style: none;
  469. .v-input {
  470. height: 40px;
  471. }
  472. }
  473. .v-input__details {
  474. padding-top: 0;
  475. padding-bottom: 3px;
  476. }
  477. .other {
  478. margin-left: 40px;
  479. border-bottom: 1px solid #333;
  480. &:focus-visible {
  481. outline: none !important;
  482. }
  483. }
  484. .submit-btn {
  485. color: #fff;
  486. background-color: var(--main-color);
  487. }
  488. }
  489. </style>