add gtag for renderer

This commit is contained in:
geekgeekrun
2025-04-13 18:55:19 +08:00
parent 3c951407ce
commit 4208996a83
18 changed files with 361 additions and 43 deletions
@@ -1,4 +1,7 @@
import { BrowserWindow, ipcMain, shell } from 'electron' import { BrowserWindow, ipcMain, shell } from 'electron'
import gtag from './gtag'
import buildInfo from '../../common/build-info.json'
import os from 'node:os'
export default function initPublicIpc() { export default function initPublicIpc() {
ipcMain.on( ipcMain.on(
@@ -23,4 +26,33 @@ export default function initPublicIpc() {
activate: true activate: true
}) })
}) })
ipcMain.on('gtag', (ev, { name, params } = {}) => {
gtag(name, {
...params,
electronLogSource: 'renderer'
})
})
ipcMain.on('send-feed-back-to-github-issue', (ev, payload) => {
const getIssueUrlWithBody = (issueBody: string = '') => {
const baseUrl = `https://github.com/geekgeekrun/geekgeekrun/issues/new`
issueBody = issueBody || ''
if (!issueBody || !issueBody.trim()) {
return baseUrl
}
const urlObj = new URL(baseUrl)
urlObj.searchParams.append('body', issueBody)
return urlObj.toString()
}
shell.openExternal(
getIssueUrlWithBody(`\n\n\n-----
版本号:${buildInfo.version}(${buildInfo.buildVersion})
提交:${buildInfo.buildHash.substring(0, 6)}
操作系统信息: \`${os.type()}\` / \`${os.release()}\` / \`${os.arch()}\``),
{
activate: true
}
)
})
} }
+7
View File
@@ -9,4 +9,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ElConfigProvider } from 'element-plus' import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs' import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { onMounted } from 'vue'
import { gtagRenderer } from './utils/gtag'
gtagRenderer('app_component_before_create')
onMounted(() => {
gtagRenderer('app_component_mounted')
})
</script> </script>
@@ -26,6 +26,7 @@
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import { sleep } from '@geekgeekrun/utils/sleep.mjs' import { sleep } from '@geekgeekrun/utils/sleep.mjs'
import { gtagRenderer } from '@renderer/utils/gtag'
const router = useRouter() const router = useRouter()
@@ -33,10 +34,12 @@ const checkDependenciesResult = ref({})
const downloadProcessWaitee = ref(null) const downloadProcessWaitee = ref(null)
onMounted(async () => { onMounted(async () => {
gtagRenderer('bootstrap_mounted')
checkDependenciesResult.value = await electron.ipcRenderer.invoke('check-dependencies') checkDependenciesResult.value = await electron.ipcRenderer.invoke('check-dependencies')
downloadProcessWaitee.value = Promise.withResolvers() downloadProcessWaitee.value = Promise.withResolvers()
if (Object.values(checkDependenciesResult.value).includes(false)) { if (Object.values(checkDependenciesResult.value).includes(false)) {
gtagRenderer('dependencies_need_download')
router.replace('/downloadingDependencies') router.replace('/downloadingDependencies')
} else { } else {
downloadProcessWaitee.value!.resolve() downloadProcessWaitee.value!.resolve()
@@ -45,6 +48,7 @@ onMounted(async () => {
downloadProcessWaitee.value!.promise.then(async () => { downloadProcessWaitee.value!.promise.then(async () => {
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file') const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
if (!isCookieFileValid) { if (!isCookieFileValid) {
gtagRenderer('found_cookie_invalid_when_bootstrap')
router.replace('/cookieAssistant') router.replace('/cookieAssistant')
} else { } else {
await sleep(1000) await sleep(1000)
@@ -13,6 +13,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onUnmounted, PropType } from 'vue' import { ref, onUnmounted, PropType } from 'vue'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { gtagRenderer } from '@renderer/utils/gtag'
const props = defineProps({ const props = defineProps({
dependenciesStatus: { dependenciesStatus: {
@@ -47,9 +48,11 @@ const processDownloadBrowser = async () => {
const promiseList: Array<Promise<void>> = [] const promiseList: Array<Promise<void>> = []
const processTasks = async () => { const processTasks = async () => {
if (!props.dependenciesStatus.puppeteerExecutableAvailable) { if (!props.dependenciesStatus.puppeteerExecutableAvailable) {
gtagRenderer('start_download_puppeteer')
const p = processDownloadBrowser() const p = processDownloadBrowser()
promiseList.push(p) promiseList.push(p)
p.then(() => { p.then(() => {
gtagRenderer('puppeteer_download_success')
props.dependenciesStatus.puppeteerExecutableAvailable = true props.dependenciesStatus.puppeteerExecutableAvailable = true
}) })
} }
@@ -64,6 +67,7 @@ const processTasks = async () => {
}) })
await p await p
} catch { } catch {
gtagRenderer('encounter_error_when_downloading_dependencies')
await ElMessageBox.confirm('需要重试吗?', '核心组件下载失败', { await ElMessageBox.confirm('需要重试吗?', '核心组件下载失败', {
closeOnClickModal: false, closeOnClickModal: false,
closeOnPressEscape: false, closeOnPressEscape: false,
@@ -72,9 +76,11 @@ const processTasks = async () => {
cancelButtonText: '退出程序' cancelButtonText: '退出程序'
}) })
.then(() => { .then(() => {
gtagRenderer('start_retry_download_dependencies')
processTasks() processTasks()
}) })
.catch(() => { .catch(() => {
gtagRenderer('cancel_download_dependencies_and_exit')
promiseList.length = 0 promiseList.length = 0
electron.ipcRenderer.invoke('exit-app-immediately') electron.ipcRenderer.invoke('exit-app-immediately')
}) })
@@ -1,6 +1,6 @@
<template> <template>
<div class="cookie-assistant-page"> <div class="cookie-assistant-page">
<div ml1em mt1em mb1em >Cookie 助手</div> <div ml1em mt1em mb1em font-size-16px>Cookie 助手</div>
<el-alert <el-alert
v-if="cookieInvalid" v-if="cookieInvalid"
type="warning" type="warning"
@@ -11,11 +11,11 @@
</el-alert> </el-alert>
<div ml1em mt1em line-height-normal> <div ml1em mt1em line-height-normal>
如果您了解如何获取Cookie了解有效的Cookie格式可以直接在下方输入框中进行编辑由于手动编辑较为麻烦建议您打开已登录过Boss直聘的浏览器使用<a 如果您了解如何获取Cookie了解有效的Cookie格式可以直接在下方输入框中进行编辑由于手动编辑较为麻烦建议您打开已登录过Boss直聘的浏览器使用<a
color-blue class="color-blue! decoration-none"
decoration-none
href="javascript:void(0)" href="javascript:void(0)"
@click.prevent="handleEditThisCookieExtensionStoreLinkClick" @click.prevent="handleEditThisCookieExtensionStoreLinkClick"
>EditThisCookie 扩展程序</a >
EditThisCookie 扩展程序 </a
>复制Cookie然后粘贴在下方输入框中文本格式为被序列化为JSON的数组不含两侧引号 >复制Cookie然后粘贴在下方输入框中文本格式为被序列化为JSON的数组不含两侧引号
</div> </div>
<br /> <br />
@@ -85,7 +85,12 @@
size="small" size="small"
type="primary" type="primary"
font-size-inherit font-size-inherit
@click="fillCollectedCookie" @click="
() => {
gtagRenderer('replace_inputted_cookie_by_collected_cookie')
fillCollectedCookie()
}
"
>使用获取到的Cookie</el-button >使用获取到的Cookie</el-button
></template ></template
></el-alert ></el-alert
@@ -103,7 +108,8 @@
import { ElForm, ElMessage } from 'element-plus' import { ElForm, ElMessage } from 'element-plus'
import { ref, onUnmounted, onMounted } from 'vue' import { ref, onUnmounted, onMounted } from 'vue'
import { checkCookieListFormat } from '../../../../common/utils/cookie' import { checkCookieListFormat } from '../../../../common/utils/cookie'
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router'
import { gtagRenderer } from '@renderer/utils/gtag'
const router = useRouter() const router = useRouter()
const cookieInvalid = ref(false) const cookieInvalid = ref(false)
@@ -138,11 +144,13 @@ const formRules = {
`JSON格式无效 - 存在语法错误: ${err.message};建议使用EditThisCookie扩展程序进行复制。` `JSON格式无效 - 存在语法错误: ${err.message};建议使用EditThisCookie扩展程序进行复制。`
) )
) )
gtagRenderer('wrong_cookie_format_for_json_syntax-error')
return return
} }
if (!checkCookieListFormat(JSON.parse(formContent.value.collectedCookies))) { if (!checkCookieListFormat(JSON.parse(formContent.value.collectedCookies))) {
cb(new Error(`Cookie格式无效 - 部分字段缺失;建议使用EditThisCookie扩展程序进行复制。`)) cb(new Error(`Cookie格式无效 - 部分字段缺失;建议使用EditThisCookie扩展程序进行复制。`))
gtagRenderer('wrong_cookie_format_for_field_loss')
return return
} }
cb() cb()
@@ -158,6 +166,9 @@ const handleCookieCollected = (_, payload) => {
collectedCookie.value = payload.cookies collectedCookie.value = payload.cookies
if (!hasUserMutateInput.value) { if (!hasUserMutateInput.value) {
fillCollectedCookie() fillCollectedCookie()
gtagRenderer('cookie_collected_and_auto_filled')
} else {
gtagRenderer('cookie_collected_but_user_has_changed_input')
} }
} }
const fillCollectedCookie = () => { const fillCollectedCookie = () => {
@@ -169,27 +180,32 @@ const fillCollectedCookie = () => {
} }
const handleClickLaunchLogin = () => { const handleClickLaunchLogin = () => {
gtagRenderer('launch_login_button_clicked')
electron.ipcRenderer.send('launch-bosszhipin-login-page-with-preload-extension') electron.ipcRenderer.send('launch-bosszhipin-login-page-with-preload-extension')
loginCookieWaitingStatus.value = LOGIN_COOKIE_WAITING_STATUS.WAITING_FOR_LOGIN loginCookieWaitingStatus.value = LOGIN_COOKIE_WAITING_STATUS.WAITING_FOR_LOGIN
} }
const handleEditThisCookieExtensionStoreLinkClick = () => { const handleEditThisCookieExtensionStoreLinkClick = () => {
gtagRenderer('etc_extension_link_clicked')
electron.ipcRenderer.send( electron.ipcRenderer.send(
'open-external-link', 'open-external-link',
'https://chromewebstore.google.com/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg' 'https://chromewebstore.google.com/detail/editthiscookie-v3/ojfebgpkimhlhcblbalbfjblapadhbol'
) )
} }
const handleCancel = () => { const handleCancel = () => {
gtagRenderer('cancel_clicked')
router.replace('/main-layout') router.replace('/main-layout')
} }
const handleSubmit = async () => { const handleSubmit = async () => {
gtagRenderer('save_clicked')
await formRef.value!.validate() await formRef.value!.validate()
await electron.ipcRenderer.invoke('write-storage-file', { await electron.ipcRenderer.invoke('write-storage-file', {
fileName: 'boss-cookies.json', fileName: 'boss-cookies.json',
data: formContent.value.collectedCookies data: formContent.value.collectedCookies
}) })
ElMessage.success('Boss直聘 Cookie 保存成功') ElMessage.success('Boss直聘 Cookie 保存成功')
gtagRenderer('save_cookie_done')
router.replace('/main-layout') router.replace('/main-layout')
} }
@@ -199,6 +215,9 @@ const handleBossZhipinLoginPageClosed = () => {
} }
} }
onMounted(() => {
gtagRenderer('cookie_assistant_mounted')
})
onMounted(async () => { onMounted(async () => {
electron.ipcRenderer.once('BOSS_ZHIPIN_COOKIE_COLLECTED', handleCookieCollected) electron.ipcRenderer.once('BOSS_ZHIPIN_COOKIE_COLLECTED', handleCookieCollected)
electron.ipcRenderer.on('BOSS_ZHIPIN_LOGIN_PAGE_CLOSED', handleBossZhipinLoginPageClosed) electron.ipcRenderer.on('BOSS_ZHIPIN_LOGIN_PAGE_CLOSED', handleBossZhipinLoginPageClosed)
@@ -226,6 +245,7 @@ onUnmounted(() => {
.cookie-assistant-page { .cookie-assistant-page {
max-width: 640px; max-width: 640px;
margin: 0 auto; margin: 0 auto;
font-size: 14px;
} }
</style> </style>
@@ -87,6 +87,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ElCheckbox, ElCheckboxGroup, ElMessage } from 'element-plus' import { ElCheckbox, ElCheckboxGroup, ElMessage } from 'element-plus'
import { ref, onMounted, onBeforeMount } from 'vue'; import { ref, onMounted, onBeforeMount } from 'vue';
import { gtagRenderer } from '@renderer/utils/gtag'
const electron = window.electron const electron = window.electron
const readmeItemCheckStatusList = ref<number[]>([]) const readmeItemCheckStatusList = ref<number[]>([])
@@ -97,8 +99,10 @@ const handleCancel = () => {
const unreadItemsAfterClickSubmit = ref<Record<number, true>>({}) const unreadItemsAfterClickSubmit = ref<Record<number, true>>({})
const handleSubmit = () => { const handleSubmit = () => {
gtagRenderer('submit_clicked')
const COUNT = 9 const COUNT = 9
if (readmeItemCheckStatusList.value.length !== COUNT) { if (readmeItemCheckStatusList.value.length !== COUNT) {
gtagRenderer('agreement_not_finish_read_tip_displayed')
ElMessage.warning( ElMessage.warning(
`您还有${COUNT - readmeItemCheckStatusList.value.length}条没有读完,读完就打勾标记一下吧` `您还有${COUNT - readmeItemCheckStatusList.value.length}条没有读完,读完就打勾标记一下吧`
) )
@@ -111,6 +115,7 @@ const handleSubmit = () => {
return return
} }
electron.ipcRenderer.invoke('first-launch-notice-approve') electron.ipcRenderer.invoke('first-launch-notice-approve')
gtagRenderer('submit_done')
} }
const handleReadmeItemCheckStatusListChange = (value: number[]) => { const handleReadmeItemCheckStatusListChange = (value: number[]) => {
value.forEach((it) => { value.forEach((it) => {
@@ -5,7 +5,16 @@
<div class="mt1em mb1em"> <div class="mt1em mb1em">
<div class="flex flex-items-center flex-justify-between"> <div class="flex flex-items-center flex-justify-between">
<div>大语言模型设置</div> <div>大语言模型设置</div>
<el-dropdown @command="(item) => openExternalLink(item.url)"> <el-dropdown
@command="
(item) => {
gtagRenderer('provider_url_for_secret_clicked', {
name: item.name
})
openExternalLink(item.url)
}
"
>
<el-button size="small" <el-button size="small"
>申请 API Secret <el-icon class="el-icon--right"><arrow-down /></el-icon >申请 API Secret <el-icon class="el-icon--right"><arrow-down /></el-icon
></el-button> ></el-button>
@@ -28,7 +37,12 @@
}" }"
href="javascript:void(0)" href="javascript:void(0)"
@click.prevent=" @click.prevent="
openExternalLink('https://api-docs.deepseek.com/zh-cn/api/create-chat-completion') () => {
gtagRenderer('chat_completion_intro_doc_link_clicked')
openExternalLink(
'https://api-docs.deepseek.com/zh-cn/api/create-chat-completion'
)
}
" "
>对话补全</a >对话补全</a
>且兼容 >且兼容
@@ -37,7 +51,12 @@
color: 'var(--el-color-primary)' color: 'var(--el-color-primary)'
}" }"
href="javascript:void(0)" href="javascript:void(0)"
@click.prevent="openExternalLink('https://www.npmjs.com/package/openai')" @click.prevent="
() => {
gtagRenderer('openai_sdk_intro_doc_link_clicked')
openExternalLink('https://www.npmjs.com/package/openai')
}
"
>OpenAI SDK</a >OpenAI SDK</a
> >
</li> </li>
@@ -158,6 +177,11 @@
maxRows: 10 maxRows: 10
}" }"
font-size-12px font-size-12px
@click="
nextTick(() =>
gtagRenderer('model_enable_status_changed', { enabled: conf.enabled })
)
"
></el-checkbox> ></el-checkbox>
</el-form-item> </el-form-item>
<el-form-item prop="serveWeight" class="ml40px"> <el-form-item prop="serveWeight" class="ml40px">
@@ -175,6 +199,11 @@
:precision="0" :precision="0"
font-size-12px font-size-12px
placeholder="0 ~ 100" placeholder="0 ~ 100"
@change="
(new_val, old_val) => {
gtagRenderer('serve_weight_changed', { new_val, old_val })
}
"
></el-input-number> ></el-input-number>
</el-form-item> </el-form-item>
</div> </div>
@@ -222,6 +251,7 @@ import {
} from 'element-plus' } from 'element-plus'
import { ArrowUp, ArrowDown, Delete } from '@element-plus/icons-vue' import { ArrowUp, ArrowDown, Delete } from '@element-plus/icons-vue'
import { ref, onMounted, watch, nextTick } from 'vue' import { ref, onMounted, watch, nextTick } from 'vue'
import { gtagRenderer } from '@renderer/utils/gtag'
interface LlmConfigItem { interface LlmConfigItem {
providerCompleteApiUrl: string providerCompleteApiUrl: string
@@ -246,10 +276,14 @@ const formContent = ref<LlmConfigItem[]>([getNewConfigItem()])
const formRules = {} const formRules = {}
const handleCancel = () => { const handleCancel = () => {
gtagRenderer('cancel_clicked')
electron.ipcRenderer.send('close-llm-config') electron.ipcRenderer.send('close-llm-config')
gtagRenderer('cancel_done')
} }
const handleSubmit = async () => { const handleSubmit = async () => {
gtagRenderer('submit_clicked', { llm_config_length: formContent.value.length })
electron.ipcRenderer.invoke('save-llm-config', JSON.parse(JSON.stringify(formContent.value))) electron.ipcRenderer.invoke('save-llm-config', JSON.parse(JSON.stringify(formContent.value)))
gtagRenderer('submit_done')
} }
onMounted(async () => { onMounted(async () => {
@@ -359,6 +393,9 @@ const providerList: Array<{ name: string; url: string }> = [
] ]
function handlePresetClick(selected: (typeof llmPresetList)[number], index) { function handlePresetClick(selected: (typeof llmPresetList)[number], index) {
gtagRenderer('model_preset_clicked', {
name: selected.name
})
for (const k of Object.keys(formContent.value[index])) { for (const k of Object.keys(formContent.value[index])) {
formContent.value[index][k] = selected.config[k] formContent.value[index][k] = selected.config[k]
} }
@@ -370,12 +407,14 @@ function addConfig() {
nextTick(() => { nextTick(() => {
firstInputRefList.value[firstInputRefList.value.length - 1]?.focus() firstInputRefList.value[firstInputRefList.value.length - 1]?.focus()
}) })
gtagRenderer('new_config_item_added')
} }
function moveConfigUp(index) { function moveConfigUp(index) {
;[formContent.value[index], formContent.value[index - 1]] = [ ;[formContent.value[index], formContent.value[index - 1]] = [
formContent.value[index - 1], formContent.value[index - 1],
formContent.value[index] formContent.value[index]
] ]
gtagRenderer('config_item_moved_up')
} }
function moveConfigDown(index) { function moveConfigDown(index) {
@@ -383,10 +422,12 @@ function moveConfigDown(index) {
formContent.value[index + 1], formContent.value[index + 1],
formContent.value[index] formContent.value[index]
] ]
gtagRenderer('config_item_moved_down')
} }
function removeConfig(index) { function removeConfig(index) {
formContent.value.splice(index, 1) formContent.value.splice(index, 1)
gtagRenderer('config_item_removed')
} }
watch( watch(
@@ -19,7 +19,17 @@
</div> </div>
<div class="flex flex-0 flex-justify-between pt10px pb10px"> <div class="flex flex-0 flex-justify-between pt10px pb10px">
<div class="w100px"> <div class="w100px">
<el-button :loading="isTableLoading" size="small" @click="getBossLibrary">刷新</el-button> <el-button
:loading="isTableLoading"
size="small"
@click="
() => {
gtagRenderer('boss_library_refresh_clicked')
getBossLibrary()
}
"
>刷新</el-button
>
</div> </div>
<ElPagination <ElPagination
v-model:current-page="pagination.pageNo" v-model:current-page="pagination.pageNo"
@@ -42,6 +52,7 @@ import { ref, onMounted, onBeforeUnmount } from 'vue'
import { ElTable, ElTableColumn, ElButton, ElPagination } from 'element-plus' import { ElTable, ElTableColumn, ElButton, ElPagination } from 'element-plus'
import { type VChatStartupLog } from '@geekgeekrun/sqlite-plugin/src/entity/VChatStartupLog' import { type VChatStartupLog } from '@geekgeekrun/sqlite-plugin/src/entity/VChatStartupLog'
import { PageReq, PagedRes } from '../../../../common/types/pagination' import { PageReq, PagedRes } from '../../../../common/types/pagination'
import { gtagRenderer } from '@renderer/utils/gtag'
const tableData = ref<VChatStartupLog[]>([]) const tableData = ref<VChatStartupLog[]>([])
const pageSizeList = ref<number[]>([100, 200, 300, 400]) const pageSizeList = ref<number[]>([100, 200, 300, 400])
@@ -54,6 +65,10 @@ const tableRef = ref<InstanceType<typeof ElTable>>()
const isTableLoading = ref(false) const isTableLoading = ref(false)
async function getBossLibrary() { async function getBossLibrary() {
try { try {
gtagRenderer('boss_library_request_sent', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
isTableLoading.value = true isTableLoading.value = true
const { data: res } = (await electron.ipcRenderer.invoke('get-boss-library', { const { data: res } = (await electron.ipcRenderer.invoke('get-boss-library', {
pageNo: pagination.value.pageNo, pageNo: pagination.value.pageNo,
@@ -65,7 +80,16 @@ async function getBossLibrary() {
pageNo: res.pageNo, pageNo: res.pageNo,
pageSize: pagination.value.pageSize pageSize: pagination.value.pageSize
} }
gtagRenderer('boss_library_request_success', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
} catch (err) { } catch (err) {
gtagRenderer('boss_library_request_error', {
err,
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
console.log(err) console.log(err)
tableData.value = [] tableData.value = []
} finally { } finally {
@@ -23,7 +23,15 @@
</div> </div>
<div class="flex flex-0 flex-justify-between pt10px pb10px"> <div class="flex flex-0 flex-justify-between pt10px pb10px">
<div class="w100px"> <div class="w100px">
<el-button :loading="isTableLoading" size="small" @click="getCompanyLibrary" <el-button
:loading="isTableLoading"
size="small"
@click="
() => {
gtagRenderer('company_library_refresh_clicked')
getCompanyLibrary()
}
"
>刷新</el-button >刷新</el-button
> >
</div> </div>
@@ -49,6 +57,7 @@ import { ElTable, ElTableColumn, ElButton, ElPagination } from 'element-plus'
import { type VChatStartupLog } from '@geekgeekrun/sqlite-plugin/src/entity/VChatStartupLog' import { type VChatStartupLog } from '@geekgeekrun/sqlite-plugin/src/entity/VChatStartupLog'
import { PageReq, PagedRes } from '../../../../common/types/pagination' import { PageReq, PagedRes } from '../../../../common/types/pagination'
import { formatCompanyScale } from '@geekgeekrun/sqlite-plugin/src/utils/parser' import { formatCompanyScale } from '@geekgeekrun/sqlite-plugin/src/utils/parser'
import { gtagRenderer } from '@renderer/utils/gtag'
const tableData = ref<VChatStartupLog[]>([]) const tableData = ref<VChatStartupLog[]>([])
const pageSizeList = ref<number[]>([100, 200, 300, 400]) const pageSizeList = ref<number[]>([100, 200, 300, 400])
@@ -64,6 +73,10 @@ const tableRef = ref<InstanceType<typeof ElTable>>()
const isTableLoading = ref(false) const isTableLoading = ref(false)
async function getCompanyLibrary() { async function getCompanyLibrary() {
try { try {
gtagRenderer('company_library_request_sent', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
isTableLoading.value = true isTableLoading.value = true
const { data: res } = (await electron.ipcRenderer.invoke('get-company-library', { const { data: res } = (await electron.ipcRenderer.invoke('get-company-library', {
pageNo: pagination.value.pageNo, pageNo: pagination.value.pageNo,
@@ -75,7 +88,16 @@ async function getCompanyLibrary() {
pageNo: res.pageNo, pageNo: res.pageNo,
pageSize: pagination.value.pageSize pageSize: pagination.value.pageSize
} }
gtagRenderer('company_library_request_success', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
} catch (err) { } catch (err) {
gtagRenderer('company_library_request_error', {
err,
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
console.log(err) console.log(err)
tableData.value = [] tableData.value = []
} finally { } finally {
@@ -66,6 +66,7 @@ import { ElForm, ElMessage } from 'element-plus'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import AnyCombineBossRecommendFilter from '@renderer/features/AnyCombineBossRecommendFilter/index.vue' import AnyCombineBossRecommendFilter from '@renderer/features/AnyCombineBossRecommendFilter/index.vue'
import { calculateTotalCombinations } from '@geekgeekrun/geek-auto-start-chat-with-boss/combineCalculator.mjs' import { calculateTotalCombinations } from '@geekgeekrun/geek-auto-start-chat-with-boss/combineCalculator.mjs'
import { gtagRenderer } from '@renderer/utils/gtag'
const router = useRouter() const router = useRouter()
const formContent = ref({ const formContent = ref({
@@ -99,13 +100,16 @@ const formRules = {
validator(_, value, cb) { validator(_, value, cb) {
if (!value) { if (!value) {
cb() cb()
gtagRenderer('empty_reg_exp_for_expect_job')
return return
} }
try { try {
new RegExp(value, 'ig') new RegExp(value, 'ig')
gtagRenderer('valid_reg_exp_for_expect_job')
cb() cb()
} catch (err) { } catch (err) {
cb(new Error(`正则无效:${err.message}`)) cb(new Error(`正则无效:${err?.message}`))
gtagRenderer('invalid_reg_exp_for_expect_job')
} }
} }
} }
@@ -113,20 +117,29 @@ const formRules = {
const formRef = ref<InstanceType<typeof ElForm>>() const formRef = ref<InstanceType<typeof ElForm>>()
const handleSubmit = async () => { const handleSubmit = async () => {
gtagRenderer('save_config_and_launch_clicked', {
has_dingtalk_robot_token: !!formContent.value?.dingtalkRobotAccessToken
})
formContent.value.expectJobRegExpStr = (formContent.value.expectJobRegExpStr || '').trim() formContent.value.expectJobRegExpStr = (formContent.value.expectJobRegExpStr || '').trim()
await formRef.value!.validate() await formRef.value!.validate()
await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value)) await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value))
router.replace({ router.replace({
path: '/geekAutoStartChatWithBoss/prepareRun', path: '/geekAutoStartChatWithBoss/prepareRun',
query: { flow: 'geek-auto-start-chat-with-boss' } query: { flow: 'geek-auto-start-chat-with-boss' }
}) })
gtagRenderer('config_saved_and_will_launch_auto_start_chat', {
has_dingtalk_robot_token: !!formContent.value?.dingtalkRobotAccessToken
})
} }
const handleSave = async () => { const handleSave = async () => {
gtagRenderer('save_config_clicked', {
has_dingtalk_robot_token: !!formContent.value?.dingtalkRobotAccessToken
})
normalizeExpectCompanies() normalizeExpectCompanies()
await formRef.value!.validate() await formRef.value!.validate()
await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value)) await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value))
ElMessage.success('Configuration saved.') ElMessage.success('配置保存成功')
gtagRenderer('config_saved')
} }
const normalizeExpectCompanies = () => { const normalizeExpectCompanies = () => {
@@ -138,6 +151,7 @@ const normalizeExpectCompanies = () => {
} }
const handleClickLaunchLogin = () => { const handleClickLaunchLogin = () => {
gtagRenderer('launch_login_clicked')
router.replace('/cookieAssistant') router.replace('/cookieAssistant')
} }
</script> </script>
@@ -55,7 +55,17 @@
</div> </div>
<div class="flex flex-0 flex-justify-between pt10px pb10px"> <div class="flex flex-0 flex-justify-between pt10px pb10px">
<div class="w100px"> <div class="w100px">
<el-button :loading="isTableLoading" size="small" @click="getJobLibrary">刷新</el-button> <el-button
:loading="isTableLoading"
size="small"
@click="
() => {
gtagRenderer('job_library_refresh_clicked')
getJobLibrary()
}
"
>刷新</el-button
>
</div> </div>
<ElPagination <ElPagination
v-model:current-page="pagination.pageNo" v-model:current-page="pagination.pageNo"
@@ -75,7 +85,12 @@
v-if="selectedJobInfoForViewSnapshot" v-if="selectedJobInfoForViewSnapshot"
:job-info="selectedJobInfoForViewSnapshot" :job-info="selectedJobInfoForViewSnapshot"
scene="jobLibrary" scene="jobLibrary"
@closed="selectedJobInfoForViewSnapshot = null" @closed="
() => {
gtagRenderer('job_info_snapshot_closed')
selectedJobInfoForViewSnapshot = null
}
"
/> />
</ElDrawer> </ElDrawer>
<ElDialog <ElDialog
@@ -93,6 +108,7 @@
:job-info-history-list="selectedJobHistory ?? []" :job-info-history-list="selectedJobHistory ?? []"
@closed=" @closed="
() => { () => {
gtagRenderer('job_library_list_dialog_closed')
selectedJobInfoForViewHistory = null selectedJobInfoForViewHistory = null
selectedJobHistory = null selectedJobHistory = null
} }
@@ -110,6 +126,7 @@ import { type JobInfoChangeLog } from '@geekgeekrun/sqlite-plugin/src/entity/Job
import { PageReq, PagedRes } from '../../../../common/types/pagination' import { PageReq, PagedRes } from '../../../../common/types/pagination'
import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue' import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue'
import JobInfoHistoryList from '../../features/JobInfoHistoryList/index.vue' import JobInfoHistoryList from '../../features/JobInfoHistoryList/index.vue'
import { gtagRenderer } from '@renderer/utils/gtag'
const tableData = ref<VChatStartupLog[]>([]) const tableData = ref<VChatStartupLog[]>([])
const pageSizeList = ref<number[]>([100, 200, 300, 400]) const pageSizeList = ref<number[]>([100, 200, 300, 400])
@@ -122,6 +139,10 @@ const tableRef = ref<InstanceType<typeof ElTable>>()
const isTableLoading = ref(false) const isTableLoading = ref(false)
async function getJobLibrary() { async function getJobLibrary() {
try { try {
gtagRenderer('job_library_request_sent', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
isTableLoading.value = true isTableLoading.value = true
const { data: res } = (await electron.ipcRenderer.invoke('get-job-library', { const { data: res } = (await electron.ipcRenderer.invoke('get-job-library', {
pageNo: pagination.value.pageNo, pageNo: pagination.value.pageNo,
@@ -133,7 +154,16 @@ async function getJobLibrary() {
pageNo: res.pageNo, pageNo: res.pageNo,
pageSize: pagination.value.pageSize pageSize: pagination.value.pageSize
} }
gtagRenderer('job_library_request_success', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
} catch (err) { } catch (err) {
gtagRenderer('job_library_request_error', {
err,
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
console.log(err) console.log(err)
tableData.value = [] tableData.value = []
} finally { } finally {
@@ -161,10 +191,12 @@ const drawVisibleModelValue = ref(false)
const selectedJobInfoForViewSnapshot = ref<VChatStartupLog | null>(null) const selectedJobInfoForViewSnapshot = ref<VChatStartupLog | null>(null)
function handleViewJobSnapshotButtonClick(record: VChatStartupLog) { function handleViewJobSnapshotButtonClick(record: VChatStartupLog) {
gtagRenderer('view_job_snapshot_button_clicked')
selectedJobInfoForViewSnapshot.value = record selectedJobInfoForViewSnapshot.value = record
drawVisibleModelValue.value = true drawVisibleModelValue.value = true
} }
async function handleViewJobOnlineButtonClick(encryptJobId: string) { async function handleViewJobOnlineButtonClick(encryptJobId: string) {
gtagRenderer('view_job_online_button_clicked')
return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', { return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', {
url: `https://www.zhipin.com/job_detail/${encryptJobId}.html` url: `https://www.zhipin.com/job_detail/${encryptJobId}.html`
}) })
@@ -174,6 +206,7 @@ const historyDialogVisibleModelValue = ref(false)
const selectedJobInfoForViewHistory = ref<VChatStartupLog | null>(null) const selectedJobInfoForViewHistory = ref<VChatStartupLog | null>(null)
const selectedJobHistory = ref<null | JobInfoChangeLog[]>(null) const selectedJobHistory = ref<null | JobInfoChangeLog[]>(null)
async function handleViewJobHistoryButtonClick(record: VChatStartupLog) { async function handleViewJobHistoryButtonClick(record: VChatStartupLog) {
gtagRenderer('view_job_history_button_clicked')
let { data: historyList } = await electron.ipcRenderer.invoke( let { data: historyList } = await electron.ipcRenderer.invoke(
'get-job-history-by-encrypt-id', 'get-job-history-by-encrypt-id',
record.encryptJobId record.encryptJobId
@@ -228,12 +261,13 @@ async function handleViewJobHistoryButtonClick(record: VChatStartupLog) {
// }) // })
if (!historyList.length) { if (!historyList.length) {
gtagRenderer('job_history_is_not_found')
ElMessage.warning({ ElMessage.warning({
message: '未找到与此条目相关的历史变更记录,再多投一投吧' message: '未找到与此条目相关的历史变更记录,再多投一投吧'
}) })
return return
} }
gtagRenderer('job_history_is_found')
historyDialogVisibleModelValue.value = true historyDialogVisibleModelValue.value = true
selectedJobInfoForViewHistory.value = record selectedJobInfoForViewHistory.value = record
selectedJobHistory.value = historyList selectedJobHistory.value = historyList
@@ -72,7 +72,15 @@
</div> </div>
<div class="flex flex-0 flex-justify-between pt10px pb10px"> <div class="flex flex-0 flex-justify-between pt10px pb10px">
<div class="w100px"> <div class="w100px">
<el-button :loading="isTableLoading" size="small" @click="getMarkAsNotSuitRecord" <el-button
:loading="isTableLoading"
size="small"
@click="
() => {
gtagRenderer('mark_as_not_suit_record_refresh_clicked')
getMarkAsNotSuitRecord()
}
"
>刷新</el-button >刷新</el-button
> >
</div> </div>
@@ -93,7 +101,12 @@
<JobInfoSnapshot <JobInfoSnapshot
v-if="selectedJobInfoForViewSnapshot" v-if="selectedJobInfoForViewSnapshot"
:job-info="selectedJobInfoForViewSnapshot" :job-info="selectedJobInfoForViewSnapshot"
@closed="selectedJobInfoForViewSnapshot = null" @closed="
() => {
gtagRenderer('mark_as_not_suit_record_closed')
selectedJobInfoForViewSnapshot = null
}
"
/> />
</ElDrawer> </ElDrawer>
</div> </div>
@@ -107,6 +120,7 @@ import { PageReq, PagedRes } from '../../../../common/types/pagination'
import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue' import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue'
import { MarkAsNotSuitReason } from '@geekgeekrun/sqlite-plugin/src/enums' import { MarkAsNotSuitReason } from '@geekgeekrun/sqlite-plugin/src/enums'
import { transformUtcDateToLocalDate } from '@geekgeekrun/utils/date.mjs' import { transformUtcDateToLocalDate } from '@geekgeekrun/utils/date.mjs'
import { gtagRenderer } from '@renderer/utils/gtag'
const tableData = ref<VMarkAsNotSuitLog[]>([]) const tableData = ref<VMarkAsNotSuitLog[]>([])
const pageSizeList = ref<number[]>([100, 200, 300, 400]) const pageSizeList = ref<number[]>([100, 200, 300, 400])
@@ -122,6 +136,10 @@ const tableRef = ref<InstanceType<typeof ElTable>>()
const isTableLoading = ref(false) const isTableLoading = ref(false)
async function getMarkAsNotSuitRecord() { async function getMarkAsNotSuitRecord() {
try { try {
gtagRenderer('mark_as_not_suit_record_request_sent', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
isTableLoading.value = true isTableLoading.value = true
const { data: res } = (await electron.ipcRenderer.invoke('get-mark-as-not-suit-record', { const { data: res } = (await electron.ipcRenderer.invoke('get-mark-as-not-suit-record', {
pageNo: pagination.value.pageNo, pageNo: pagination.value.pageNo,
@@ -133,7 +151,16 @@ async function getMarkAsNotSuitRecord() {
pageNo: res.pageNo, pageNo: res.pageNo,
pageSize: pagination.value.pageSize pageSize: pagination.value.pageSize
} }
gtagRenderer('mark_as_not_suit_record_request_success', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
} catch (err) { } catch (err) {
gtagRenderer('mark_as_not_suit_record_request_error', {
err,
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
console.log(err) console.log(err)
tableData.value = [] tableData.value = []
} finally { } finally {
@@ -158,6 +185,7 @@ onMounted(() => {
}) })
async function handleViewJobOnlineButtonClick(encryptJobId: string) { async function handleViewJobOnlineButtonClick(encryptJobId: string) {
gtagRenderer('view_job_online_button_clicked')
return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', { return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', {
url: `https://www.zhipin.com/job_detail/${encryptJobId}.html` url: `https://www.zhipin.com/job_detail/${encryptJobId}.html`
}) })
@@ -167,6 +195,7 @@ const drawVisibleModelValue = ref(false)
const selectedJobInfoForViewSnapshot = ref<VMarkAsNotSuitLog | null>(null) const selectedJobInfoForViewSnapshot = ref<VMarkAsNotSuitLog | null>(null)
function handleViewJobSnapshotButtonClick(record: VMarkAsNotSuitLog) { function handleViewJobSnapshotButtonClick(record: VMarkAsNotSuitLog) {
gtagRenderer('view_job_snapshot_button_clicked')
selectedJobInfoForViewSnapshot.value = record selectedJobInfoForViewSnapshot.value = record
drawVisibleModelValue.value = true drawVisibleModelValue.value = true
} }
@@ -73,7 +73,16 @@
<el-button size="small" type="primary" @click="handleClickEditPrompt"> <el-button size="small" type="primary" @click="handleClickEditPrompt">
使用外部编辑器编辑提示词模板 使用外部编辑器编辑提示词模板
</el-button> </el-button>
<el-button size="small" type="primary" @click="restoreDefaultTemplate"> <el-button
size="small"
type="primary"
@click="
() => {
gtagRenderer('reset_template_clicked_in_main_form')
restoreDefaultTemplate()
}
"
>
还原默认提示词模板 还原默认提示词模板
</el-button> </el-button>
</div> </div>
@@ -139,6 +148,8 @@ import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
import { dayjs, ElForm, ElMessage, ElMessageBox } from 'element-plus' import { dayjs, ElForm, ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { RECHAT_CONTENT_SOURCE } from '../../../../common/enums/auto-start-chat' import { RECHAT_CONTENT_SOURCE } from '../../../../common/enums/auto-start-chat'
import { gtagRenderer } from '@renderer/utils/gtag'
const router = useRouter() const router = useRouter()
const formContent = ref({ const formContent = ref({
autoReminder: { autoReminder: {
@@ -155,8 +166,10 @@ const enableRechatLimit = computed({
}, },
set(val) { set(val) {
if (!val) { if (!val) {
gtagRenderer('rechat_limit_disabled')
formContent.value.autoReminder.rechatLimitDay = 0 formContent.value.autoReminder.rechatLimitDay = 0
} else { } else {
gtagRenderer('rechat_limit_enabled')
formContent.value.autoReminder.rechatLimitDay = 21 formContent.value.autoReminder.rechatLimitDay = 21
} }
} }
@@ -213,12 +226,17 @@ watch(
) )
const handleSubmit = async () => { const handleSubmit = async () => {
gtagRenderer('save_config_clicked', {
...formContent.value?.autoReminder
})
await formRef.value!.validate() await formRef.value!.validate()
await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value)) await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value))
gtagRenderer('config_saved')
try { try {
await electron.ipcRenderer.invoke('check-if-auto-remind-prompt-valid') await electron.ipcRenderer.invoke('check-if-auto-remind-prompt-valid')
} catch (err) { } catch (err) {
if (err?.message?.includes(`RESUME_PLACEHOLDER_NOT_EXIST`)) { if (err?.message?.includes(`RESUME_PLACEHOLDER_NOT_EXIST`)) {
gtagRenderer('cannot_launch_due_to_resume_placeholder_not_exist')
console.log(`提示词模板无效`, err) console.log(`提示词模板无效`, err)
ElMessageBox.confirm( ElMessageBox.confirm(
'提示词模板缺少简历内容占位符:<br /><b>__REPLACE_REAL_RESUME_HERE__</b><br /><br />您是否希望还原默认的提示词模板?', '提示词模板缺少简历内容占位符:<br /><b>__REPLACE_REAL_RESUME_HERE__</b><br /><br />您是否希望还原默认的提示词模板?',
@@ -232,10 +250,14 @@ const handleSubmit = async () => {
} }
) )
.then(async () => { .then(async () => {
gtagRenderer('reset_template_clicked_in_invalid_tip_dialog')
await restoreDefaultTemplate() await restoreDefaultTemplate()
}) })
.catch(() => {}) .catch(() => {
gtagRenderer('close_invalid_tip_dialog')
})
} else { } else {
gtagRenderer('cannot_launch_due_to_unknown_error', { err })
ElMessage({ ElMessage({
type: 'error', type: 'error',
message: '用于生成自动提醒消息的提示词检查未通过,请重试' message: '用于生成自动提醒消息的提示词检查未通过,请重试'
@@ -243,6 +265,7 @@ const handleSubmit = async () => {
} }
return return
} }
gtagRenderer('reminder_launched')
router.replace({ router.replace({
path: '/geekAutoStartChatWithBoss/prepareRun', path: '/geekAutoStartChatWithBoss/prepareRun',
query: { flow: 'read-no-reply-reminder' } query: { flow: 'read-no-reply-reminder' }
@@ -287,6 +310,7 @@ const rechatLimitDateString = computed(() => {
}) })
const handleClickConfigLlm = async () => { const handleClickConfigLlm = async () => {
gtagRenderer('config_llm_clicked')
try { try {
await electron.ipcRenderer.invoke('llm-config') await electron.ipcRenderer.invoke('llm-config')
} catch (err) { } catch (err) {
@@ -295,6 +319,7 @@ const handleClickConfigLlm = async () => {
} }
const handleClickEditResume = async () => { const handleClickEditResume = async () => {
gtagRenderer('edit_resume_clicked')
try { try {
await electron.ipcRenderer.invoke('resume-edit') await electron.ipcRenderer.invoke('resume-edit')
} catch (err) { } catch (err) {
@@ -303,6 +328,7 @@ const handleClickEditResume = async () => {
} }
const handleClickEditPrompt = async () => { const handleClickEditPrompt = async () => {
gtagRenderer('edit_prompt_clicked')
await electron.ipcRenderer.send('no-reply-reminder-prompt-edit') await electron.ipcRenderer.send('no-reply-reminder-prompt-edit')
} }
</script> </script>
@@ -55,7 +55,15 @@
</div> </div>
<div class="flex flex-0 flex-justify-between pt10px pb10px"> <div class="flex flex-0 flex-justify-between pt10px pb10px">
<div class="w100px"> <div class="w100px">
<el-button :loading="isTableLoading" size="small" @click="getAutoStartChatRecord" <el-button
:loading="isTableLoading"
size="small"
@click="
() => {
gtagRenderer('start_chat_record_refresh_clicked')
getAutoStartChatRecord()
}
"
>刷新</el-button >刷新</el-button
> >
</div> </div>
@@ -76,7 +84,12 @@
<JobInfoSnapshot <JobInfoSnapshot
v-if="selectedJobInfoForViewSnapshot" v-if="selectedJobInfoForViewSnapshot"
:job-info="selectedJobInfoForViewSnapshot" :job-info="selectedJobInfoForViewSnapshot"
@closed="selectedJobInfoForViewSnapshot = null" @closed="
() => {
gtagRenderer('start_chat_record_closed')
selectedJobInfoForViewSnapshot = null
}
"
/> />
</ElDrawer> </ElDrawer>
</div> </div>
@@ -89,6 +102,7 @@ import { type VChatStartupLog } from '@geekgeekrun/sqlite-plugin/src/entity/VCha
import { transformUtcDateToLocalDate } from '@geekgeekrun/utils/date.mjs' import { transformUtcDateToLocalDate } from '@geekgeekrun/utils/date.mjs'
import { PageReq, PagedRes } from '../../../../common/types/pagination' import { PageReq, PagedRes } from '../../../../common/types/pagination'
import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue' import JobInfoSnapshot from '../../features/JobInfoSnapshot/index.vue'
import { gtagRenderer } from '@renderer/utils/gtag'
const tableData = ref<VChatStartupLog[]>([]) const tableData = ref<VChatStartupLog[]>([])
const pageSizeList = ref<number[]>([100, 200, 300, 400]) const pageSizeList = ref<number[]>([100, 200, 300, 400])
@@ -104,6 +118,10 @@ const tableRef = ref<InstanceType<typeof ElTable>>()
const isTableLoading = ref(false) const isTableLoading = ref(false)
async function getAutoStartChatRecord() { async function getAutoStartChatRecord() {
try { try {
gtagRenderer('start_chat_record_request_sent', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
isTableLoading.value = true isTableLoading.value = true
const { data: res } = (await electron.ipcRenderer.invoke('get-auto-start-chat-record', { const { data: res } = (await electron.ipcRenderer.invoke('get-auto-start-chat-record', {
pageNo: pagination.value.pageNo, pageNo: pagination.value.pageNo,
@@ -115,7 +133,16 @@ async function getAutoStartChatRecord() {
pageNo: res.pageNo, pageNo: res.pageNo,
pageSize: pagination.value.pageSize pageSize: pagination.value.pageSize
} }
gtagRenderer('start_chat_record_request_success', {
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
} catch (err) { } catch (err) {
gtagRenderer('start_chat_record_request_error', {
err,
page_no: pagination.value.pageNo,
page_size: pagination.value.pageSize,
})
console.log(err) console.log(err)
tableData.value = [] tableData.value = []
} finally { } finally {
@@ -41,6 +41,7 @@ import { ElIcon } from 'element-plus'
import { TopRight } from '@element-plus/icons-vue' import { TopRight } from '@element-plus/icons-vue'
import useBuildInfo from '@renderer/hooks/useBuildInfo' import useBuildInfo from '@renderer/hooks/useBuildInfo'
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'
import { gtagRenderer } from '@renderer/utils/gtag'
const router = useRouter() const router = useRouter()
const unmountedCbs: Array<InstanceType<typeof Function>> = [] const unmountedCbs: Array<InstanceType<typeof Function>> = []
onUnmounted(() => { onUnmounted(() => {
@@ -76,31 +77,18 @@ onUnmounted(() => {
})() })()
const { buildInfo } = useBuildInfo() const { buildInfo } = useBuildInfo()
const getIssueUrlWithBody = (issueBody: string = '') => {
const baseUrl = `https://github.com/geekgeekrun/geekgeekrun/issues/new`
issueBody = issueBody || ''
if (!issueBody || !issueBody.trim()) {
return baseUrl
}
const urlObj = new URL(baseUrl)
urlObj.searchParams.append('body', issueBody)
return urlObj.toString()
}
const handleFeedbackClick = () => { const handleFeedbackClick = () => {
electron.ipcRenderer.send( gtagRenderer('goto_feedback_clicked')
'open-external-link', electron.ipcRenderer.send('send-feed-back-to-github-issue')
getIssueUrlWithBody(`\n\n\n-----
版本号:${buildInfo.value.version}(${buildInfo.value.buildVersion})
提交:${buildInfo.value.buildHash.substring(0, 6)}`)
)
} }
const handleGotoProjectPageClick = () => { const handleGotoProjectPageClick = () => {
gtagRenderer('goto_project_github_clicked')
electron.ipcRenderer.send('open-external-link', 'https://github.com/geekgeekrun/geekgeekrun') electron.ipcRenderer.send('open-external-link', 'https://github.com/geekgeekrun/geekgeekrun')
} }
const handleLaunchBossSite = debounce( const handleLaunchBossSite = debounce(
async () => { async () => {
gtagRenderer('launch-boss-site-clicked')
return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', { return await electron.ipcRenderer.invoke('open-site-with-boss-cookie', {
url: `https://www.zhipin.com/` url: `https://www.zhipin.com/`
}) })
@@ -313,6 +313,7 @@
import { ElForm, ElButton, ElAlert } from 'element-plus' import { ElForm, ElButton, ElAlert } from 'element-plus'
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ArrowUp, ArrowDown, Delete, Plus } from '@element-plus/icons-vue' import { ArrowUp, ArrowDown, Delete, Plus } from '@element-plus/icons-vue'
import { gtagRenderer } from '@renderer/utils/gtag'
interface ResumeContent { interface ResumeContent {
name: string name: string
@@ -360,10 +361,14 @@ const formContent = ref<ResumeContent>(getEmptyFormContent())
const formRules = {} const formRules = {}
const handleCancel = () => { const handleCancel = () => {
gtagRenderer('cancel_clicked')
electron.ipcRenderer.send('close-resume-editor') electron.ipcRenderer.send('close-resume-editor')
gtagRenderer('cancel_done')
} }
const handleSubmit = async () => { const handleSubmit = async () => {
gtagRenderer('submit_clicked')
electron.ipcRenderer.invoke('save-resume-content', JSON.parse(JSON.stringify(formContent.value))) electron.ipcRenderer.invoke('save-resume-content', JSON.parse(JSON.stringify(formContent.value)))
gtagRenderer('submit_done')
} }
onMounted(async () => { onMounted(async () => {
@@ -410,12 +415,14 @@ function getNewWorkExpItem() {
} }
function addWorkExp() { function addWorkExp() {
formContent.value.geekWorkExpList.push(getNewWorkExpItem()) formContent.value.geekWorkExpList.push(getNewWorkExpItem())
gtagRenderer('resume_work_exp_added')
} }
function moveWorkExpUp(index) { function moveWorkExpUp(index) {
;[formContent.value.geekWorkExpList[index], formContent.value.geekWorkExpList[index - 1]] = [ ;[formContent.value.geekWorkExpList[index], formContent.value.geekWorkExpList[index - 1]] = [
formContent.value.geekWorkExpList[index - 1], formContent.value.geekWorkExpList[index - 1],
formContent.value.geekWorkExpList[index] formContent.value.geekWorkExpList[index]
] ]
gtagRenderer('resume_work_exp_moved_up')
} }
function moveWorkExpDown(index) { function moveWorkExpDown(index) {
@@ -423,10 +430,12 @@ function moveWorkExpDown(index) {
formContent.value.geekWorkExpList[index + 1], formContent.value.geekWorkExpList[index + 1],
formContent.value.geekWorkExpList[index] formContent.value.geekWorkExpList[index]
] ]
gtagRenderer('resume_work_exp_moved_down')
} }
function removeWorkExp(index) { function removeWorkExp(index) {
formContent.value.geekWorkExpList.splice(index, 1) formContent.value.geekWorkExpList.splice(index, 1)
gtagRenderer('resume_work_exp_removed')
} }
// #endregion // #endregion
@@ -443,12 +452,14 @@ function getNewProjExpItem() {
} }
function addProjExp() { function addProjExp() {
formContent.value.geekProjExpList.push(getNewProjExpItem()) formContent.value.geekProjExpList.push(getNewProjExpItem())
gtagRenderer('resume_proj_exp_added')
} }
function moveProjExpUp(index) { function moveProjExpUp(index) {
;[formContent.value.geekProjExpList[index], formContent.value.geekProjExpList[index - 1]] = [ ;[formContent.value.geekProjExpList[index], formContent.value.geekProjExpList[index - 1]] = [
formContent.value.geekProjExpList[index - 1], formContent.value.geekProjExpList[index - 1],
formContent.value.geekProjExpList[index] formContent.value.geekProjExpList[index]
] ]
gtagRenderer('resume_proj_exp_moved_up')
} }
function moveProjExpDown(index) { function moveProjExpDown(index) {
@@ -456,12 +467,18 @@ function moveProjExpDown(index) {
formContent.value.geekProjExpList[index + 1], formContent.value.geekProjExpList[index + 1],
formContent.value.geekProjExpList[index] formContent.value.geekProjExpList[index]
] ]
gtagRenderer('resume_proj_exp_moved_down')
} }
function removeProjExp(index) { function removeProjExp(index) {
formContent.value.geekProjExpList.splice(index, 1) formContent.value.geekProjExpList.splice(index, 1)
gtagRenderer('resume_proj_exp_removed')
} }
// #endregion // #endregion
onMounted(() => {
gtagRenderer('resume_editor_mounted')
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
+7 -2
View File
@@ -1,5 +1,6 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import BootstrapSplash from '@renderer/page/BootstrapSplash/index.vue' import BootstrapSplash from '@renderer/page/BootstrapSplash/index.vue'
import { gtagRenderer } from '@renderer/utils/gtag'
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
@@ -128,7 +129,7 @@ const routes: Array<RouteRecordRaw> = [
component: () => import('@renderer/page/BootstrapSplash/page/DownloadingDependencies.vue'), component: () => import('@renderer/page/BootstrapSplash/page/DownloadingDependencies.vue'),
meta: { meta: {
title: '正在下载核心组件' title: '正在下载核心组件'
}, }
} }
] ]
} }
@@ -139,12 +140,16 @@ const router = createRouter({
routes routes
}) })
router.afterEach((to) => { router.afterEach((to, from) => {
if (to.meta?.title) { if (to.meta?.title) {
document.title = `${to.meta.title} - GeekGeekRun` document.title = `${to.meta.title} - GeekGeekRun`
} else { } else {
document.title = `GeekGeekRun` document.title = `GeekGeekRun`
} }
gtagRenderer('router_path_changed', {
from_path: from.fullPath,
to_path: to.fullPath
})
}) })
export default router export default router
@@ -0,0 +1,17 @@
export function gtagRenderer(name, params: any = null) {
try {
electron.ipcRenderer.send('gtag', {
name,
params: {
...(params ?? {}),
user_agent: navigator.userAgent,
current_href: location.href,
screen_w: window.screen?.width ?? null,
screen_h: window.screen?.height ?? null,
screen_dpr: window.devicePixelRatio
}
})
} catch (err) {
console.log('gtag error', err)
}
}