YTViews.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. <script setup lang="ts">
  2. import { ref, reactive, computed } 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. company: "",
  86. url: "",
  87. area: "",
  88. language: "",
  89. ages: [],
  90. target: "",
  91. theme: "",
  92. taxID: "",
  93. });
  94. // 其他選項
  95. let target = ref("");
  96. let theme = ref("");
  97. let otherTarget = ref("");
  98. let otherTheme = ref("");
  99. // 檢查必填欄位
  100. const isSubmitDisabled = computed(() => {
  101. return (
  102. !userData.email ||
  103. !userData.name ||
  104. !userData.url ||
  105. !userData.area ||
  106. !userData.language ||
  107. !target.value ||
  108. !theme.value
  109. );
  110. });
  111. async function ECPaySubmit() {
  112. if (!assignPrice.value) {
  113. chooseError.value = true;
  114. window.scrollTo({ top: 0, behavior: "smooth" });
  115. } else {
  116. chooseError.value = false;
  117. }
  118. if (target.value === "其他" && otherTarget.value !== "") {
  119. userData.target = otherTarget.value;
  120. } else {
  121. userData.target = target.value;
  122. }
  123. if (theme.value === "其他" && otherTheme.value !== "") {
  124. userData.theme = otherTheme.value;
  125. } else {
  126. userData.theme = theme.value;
  127. }
  128. let lang = ref("");
  129. let getLang = localStorage.getItem("lang");
  130. // 綠界顯示語言
  131. if (!getLang || getLang === "zh") {
  132. lang.value = "ZH";
  133. } else {
  134. lang.value = "ENG";
  135. }
  136. let data: YTViewsUserData = {
  137. item: `YT0.4-(${assignView.value})`,
  138. amount: 5,
  139. email: userData.email,
  140. name: userData.name,
  141. company: userData.company,
  142. url: userData.url,
  143. area: userData.area,
  144. language: userData.language,
  145. ages: userData.ages.join(","),
  146. target: userData.target,
  147. theme: userData.theme,
  148. taxID: userData.taxID,
  149. };
  150. const originalHTML = await mainStore.YTViewsPayment(data, lang.value);
  151. let formHTML = originalHTML?.replace(
  152. '<script type="text/javascript">document.getElementById("data_set").submit();</scr',
  153. ""
  154. );
  155. formHTML = formHTML?.replace("ipt>", "");
  156. const payFormElement = document.getElementById("pay-form");
  157. payFormElement!.innerHTML = formHTML!;
  158. const ecpayForm: HTMLFormElement = <HTMLFormElement>(
  159. document.getElementById("data_set")
  160. );
  161. console.log(ecpayForm);
  162. ecpayForm.submit();
  163. }
  164. // 資料存進 Google Sheets
  165. // async function saveData() {
  166. // const scriptURL =
  167. // "https://script.google.com/macros/s/AKfycbxCcfiOQ695DaxIa3peClqRRTWNj2aUNLbx7ty8U2wKlyU7wreQLioHG-sls5MPKBdlRQ/exec";
  168. // let formdata = new FormData();
  169. // formdata.append("email", userData.email);
  170. // formdata.append("name", userData.name);
  171. // formdata.append("company", userData.company);
  172. // formdata.append("url", userData.url);
  173. // formdata.append("area", userData.area);
  174. // formdata.append("language", userData.language);
  175. // formdata.append("age", userData.age.join("、"));
  176. // formdata.append("object", userData.object);
  177. // formdata.append("theme", userData.theme);
  178. // formdata.append("tax", userData.tax);
  179. // axios
  180. // .post(scriptURL, formdata)
  181. // .then(function (response) {
  182. // console.log(response.data);
  183. // })
  184. // .catch(function (error) {
  185. // console.log(error);
  186. // });
  187. // }
  188. </script>
  189. <template>
  190. <Navbar />
  191. <v-container fluid class="mt-16">
  192. <v-card class="ma-3 pa-3">
  193. <v-card-title primary-title class="mb-3">
  194. <h3 class="headline primary--text">YouTube 觀看數</h3>
  195. </v-card-title>
  196. <v-card-text>
  197. <p class="ms-3">請選擇方案:</p>
  198. <v-row no-gutters class="pay-card">
  199. <v-col
  200. xs="12"
  201. sm="6"
  202. lg="3"
  203. v-for="(item, index) in cardItems"
  204. :key="index"
  205. >
  206. <button @click="activeBtn(item.view, item.param)" class="w-100">
  207. <v-card
  208. class="ma-3 py-3"
  209. :class="{ active: assignPrice === item.param }"
  210. >
  211. <v-card-title primary-title class="pa-0">
  212. <div class="d-flex flex-column">
  213. <section class="d-flex mx-auto">
  214. <img
  215. width="30"
  216. height="30"
  217. src="@/assets/img/icon/play-button.png"
  218. alt=""
  219. class="me-2"
  220. />
  221. <h5 class="m-0">{{ item.view }}</h5>
  222. </section>
  223. <span class="text-center" style="color: #7c8ba7"
  224. >Views</span
  225. >
  226. </div>
  227. <p class="price">
  228. NT${{ item.price }} <br />
  229. <small>NT${{ item.originalPrice }}</small>
  230. </p>
  231. </v-card-title>
  232. <v-card-text class="d-flex align-center justify-center mt-3">
  233. <ul>
  234. <li
  235. v-for="(item, index) in items"
  236. :key="index"
  237. class="d-flex align-center"
  238. >
  239. <img
  240. width="30"
  241. src="@/assets/img/icon/check.png"
  242. alt=""
  243. />
  244. {{ item.title }}
  245. </li>
  246. </ul>
  247. </v-card-text>
  248. <!-- <v-card-actions class="d-flex justify-center">
  249. <v-btn @click="ECPaySubmit(item.param)"> Buy Now </v-btn>
  250. </v-card-actions> -->
  251. </v-card>
  252. </button>
  253. </v-col>
  254. <p class="ms-3 error" v-show="chooseError">尚未選擇方案</p>
  255. </v-row>
  256. <v-sheet max-width="500" class="mx-auto mt-10">
  257. <v-form @submit.prevent class="ECPay-form">
  258. <v-text-field
  259. v-model="userData.email"
  260. :rules="emailRules()"
  261. label="電子郵件"
  262. required
  263. ></v-text-field>
  264. <v-text-field
  265. v-model="userData.name"
  266. :rules="required()"
  267. label="姓名"
  268. required
  269. ></v-text-field>
  270. <v-text-field
  271. v-model="userData.company"
  272. label="公司 / 所屬產業"
  273. ></v-text-field>
  274. <v-text-field
  275. v-model="userData.url"
  276. :rules="urlRules"
  277. label="YouTube 影片網址"
  278. required
  279. ></v-text-field>
  280. <v-text-field
  281. v-model="userData.area"
  282. :rules="required()"
  283. label="影片放送地區(國家 / 縣市)"
  284. required
  285. ></v-text-field>
  286. <v-text-field
  287. v-model="userData.language"
  288. :rules="required()"
  289. label="受眾語言"
  290. required
  291. ></v-text-field>
  292. <p class="mt-5">客層(未選擇的話視為全部)</p>
  293. <div class="checkbox ms-5">
  294. <v-checkbox
  295. v-for="option in ageOptions"
  296. v-model="userData.ages"
  297. :key="option.label"
  298. :label="option.label"
  299. :value="option.label"
  300. color="primary"
  301. ></v-checkbox>
  302. </div>
  303. <p class="mt-10 mb-3">
  304. 目標對象區隔(興趣、習慣)<span class="text-red-darken-1">*</span>
  305. </p>
  306. <div class="ms-5">
  307. <v-radio-group v-model="target">
  308. <v-radio
  309. v-for="option in objectOptions"
  310. :key="option.label"
  311. :label="option.label"
  312. :value="option.label"
  313. color="primary"
  314. ></v-radio>
  315. <v-radio label="其他" value="其他" color="primary"></v-radio>
  316. <input v-model="otherTarget" type="text" class="other" />
  317. </v-radio-group>
  318. </div>
  319. <p class="mt-5 mb-3">
  320. 影片主題 <span class="text-red-darken-1">*</span>
  321. </p>
  322. <div class="ms-5">
  323. <v-radio-group v-model="theme">
  324. <v-radio
  325. v-for="option in themeOptions"
  326. :key="option.label"
  327. :label="option.label"
  328. :value="option.label"
  329. color="primary"
  330. ></v-radio>
  331. <v-radio label="其他" value="其他" color="primary"></v-radio>
  332. <input v-model="otherTheme" type="text" class="other" />
  333. </v-radio-group>
  334. </div>
  335. <v-text-field
  336. type="number"
  337. label="是否需要統編(可填寫統編號碼)"
  338. v-model="userData.taxID"
  339. ></v-text-field>
  340. <v-btn
  341. @click="ECPaySubmit()"
  342. type="submit"
  343. block
  344. class="mt-2 submit-btn"
  345. :disabled="isSubmitDisabled"
  346. >送出</v-btn
  347. >
  348. </v-form>
  349. </v-sheet>
  350. </v-card-text>
  351. </v-card>
  352. </v-container>
  353. <div id="pay-form"></div>
  354. </template>
  355. <style lang="scss">
  356. .pay-card {
  357. .v-card-title {
  358. h5 {
  359. font-size: 20px;
  360. }
  361. span {
  362. font-size: 16px;
  363. letter-spacing: 1px;
  364. }
  365. .price {
  366. padding: 10px 0;
  367. font-size: 26px;
  368. font-weight: 600;
  369. text-align: center;
  370. color: #fff;
  371. background-color: var(--main-color);
  372. letter-spacing: 2px;
  373. small {
  374. display: block;
  375. margin-top: -3px;
  376. font-size: 18px;
  377. font-weight: 100;
  378. text-decoration: line-through;
  379. }
  380. }
  381. }
  382. .v-card-text {
  383. ul {
  384. padding: 0;
  385. list-style: none;
  386. }
  387. }
  388. .v-card-actions {
  389. button {
  390. padding: 5px 15px;
  391. color: #fff;
  392. border-radius: 100px;
  393. background-color: var(--main-color);
  394. border: 1px solid transparent;
  395. &:hover {
  396. color: var(--main-color);
  397. background-color: #fff;
  398. border: 1px solid var(--main-color);
  399. }
  400. }
  401. }
  402. .active {
  403. border: 3px solid var(--main-color);
  404. }
  405. .error {
  406. color: #b00020;
  407. }
  408. }
  409. .ECPay-form {
  410. font-size: 16px;
  411. .checkbox {
  412. margin-left: 5px;
  413. list-style: none;
  414. .v-input {
  415. height: 40px;
  416. }
  417. }
  418. .v-input__details {
  419. padding-top: 0;
  420. padding-bottom: 3px;
  421. }
  422. .other {
  423. margin-left: 40px;
  424. border-bottom: 1px solid #333;
  425. &:focus-visible {
  426. outline: none !important;
  427. }
  428. }
  429. .submit-btn {
  430. color: #fff;
  431. background-color: var(--main-color);
  432. }
  433. }
  434. </style>