diff --git a/src/api/mp/mpuser/index.ts b/src/api/mp/user/index.ts
similarity index 100%
rename from src/api/mp/mpuser/index.ts
rename to src/api/mp/user/index.ts
diff --git a/src/views/mp/mpuser/index.vue b/src/views/mp/mpuser/index.vue
deleted file mode 100644
index d773760d..00000000
--- a/src/views/mp/mpuser/index.vue
+++ /dev/null
@@ -1,293 +0,0 @@
-<template>
-  <div class="app-container">
-    <doc-alert title="公众号粉丝" url="https://doc.iocoder.cn/mp/user/" />
-
-    <!-- 搜索工作栏 -->
-    <el-form
-      :model="queryParams"
-      ref="queryFormRef"
-      size="small"
-      :inline="true"
-      v-show="showSearch"
-      label-width="68px"
-    >
-      <el-form-item label="公众号" prop="accountId">
-        <el-select v-model="queryParams.accountId" placeholder="请选择公众号">
-          <el-option
-            v-for="item in accounts"
-            :key="parseInt(item.id)"
-            :label="item.name"
-            :value="parseInt(item.id)"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="用户标识" prop="openid">
-        <el-input
-          v-model="queryParams.openid"
-          placeholder="请输入用户标识"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="昵称" prop="nickname">
-        <el-input
-          v-model="queryParams.nickname"
-          placeholder="请输入昵称"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
-      </el-form-item>
-    </el-form>
-
-    <!-- 操作工具栏 -->
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="info"
-          plain
-          icon="el-icon-refresh"
-          size="small"
-          @click="handleSync"
-          v-hasPermi="['mp:user:sync']"
-          >同步
-        </el-button>
-      </el-col>
-      <!-- <right-toolbar :showSearch="showSearch" @query-table="getList" /> -->
-    </el-row>
-
-    <!-- 列表 -->
-    <ContentWrap>
-      <el-table v-loading="loading" :data="list">
-        <el-table-column label="编号" align="center" prop="id" />
-        <el-table-column label="用户标识" align="center" prop="openid" width="260" />
-        <el-table-column label="昵称" align="center" prop="nickname" />
-        <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column label="标签" align="center" prop="tagIds" width="200">
-          <template #default="scope">
-            <span v-for="(tagId, index) in scope.row.tagIds" :key="index">
-              <el-tag>{{ tags.find((tag) => tag.tagId === tagId)?.name }} </el-tag>&nbsp;
-            </span>
-          </template>
-        </el-table-column>
-        <el-table-column label="订阅状态" align="center" prop="subscribeStatus">
-          <template #default="scope">
-            <el-tag v-if="scope.row.subscribeStatus === 0" type="success">已订阅</el-tag>
-            <el-tag v-else type="danger">未订阅</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="订阅时间" align="center" prop="subscribeTime" width="180">
-          <template #default="scope">
-            <span>{{ formatDate(scope.row.subscribeTime) }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center">
-          <template #default="scope">
-            <div class="flex justify-center items-center">
-              <el-button
-                type="primary"
-                link
-                @click="handleUpdate(scope.row)"
-                v-hasPermi="['mp:user:update']"
-              >
-                <Icon icon="ep:edit" />修改
-              </el-button>
-            </div>
-          </template>
-        </el-table-column>
-      </el-table>
-    </ContentWrap>
-
-    <!-- 分页组件 -->
-    <pagination
-      v-show="total > 0"
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-
-    <!-- 对话框(添加 / 修改) -->
-    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-      <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="昵称" prop="nickname">
-          <el-input v-model="form.nickname" placeholder="请输入昵称" />
-        </el-form-item>
-        <el-form-item label="备注" prop="remark">
-          <el-input v-model="form.remark" placeholder="请输入备注" />
-        </el-form-item>
-        <el-form-item label="标签" prop="tagIds">
-          <el-select v-model="form.tagIds" multiple clearable placeholder="请选择标签">
-            <el-option
-              v-for="item in tags"
-              :key="parseInt(item.tagId)"
-              :label="item.name"
-              :value="parseInt(item.tagId)"
-            />
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitForm">确 定</el-button>
-          <el-button @click="cancel">取 消</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script lang="ts" setup name="MpUser">
-import { ref, reactive } from 'vue'
-import { updateUser, getUser, getUserPage, syncUser } from '@/api/mp/mpuser'
-import { getSimpleAccountList } from '@/api/mp/account'
-import { getSimpleTagList } from '@/api/mp/tag'
-import { formatDate } from '@/utils/formatTime'
-
-const message = useMessage()
-
-const formRef = ref()
-const queryFormRef = ref()
-
-// 遮罩层
-const loading = ref(true)
-// 显示搜索条件
-const showSearch = ref(true)
-// 总条数
-const total = ref(0)
-// 微信公众号粉丝列表
-const list = ref([])
-// 弹出层标题
-const title = ref('')
-// 是否显示弹出层
-const open = ref(false)
-// 查询参数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  accountId: null,
-  openid: null,
-  nickname: null
-})
-// 表单参数
-const form = ref({})
-// 表单校验
-const rules = ref({})
-
-// 公众号账号列表
-const accounts = ref([])
-// 公众号标签列表
-const tags = ref([])
-
-onMounted(() => {
-  getSimpleAccountList().then((data) => {
-    accounts.value = data
-    // 默认选中第一个
-    if (accounts.value.length > 0) {
-      queryParams.accountId = accounts.value[0].id
-    }
-    // 加载数据
-    getList()
-  })
-
-  // 加载标签
-  getSimpleTagList().then((data) => {
-    tags.value = data
-  })
-})
-
-/** 查询列表 */
-const getList = () => {
-  // 如果没有选中公众号账号,则进行提示。
-  if (!queryParams.accountId) {
-    message.error('未选中公众号,无法查询用户')
-    return false
-  }
-
-  loading.value = true
-  // 处理查询参数
-  let params = { ...queryParams }
-  // 执行查询
-  getUserPage(params)
-    .then((data) => {
-      list.value = data.list
-      total.value = data.total
-    })
-    .finally(() => {
-      loading.value = false
-    })
-}
-
-/** 取消按钮 */
-const cancel = () => {
-  open.value = false
-  reset()
-}
-
-/** 表单重置 */
-const reset = () => {
-  form.value = {
-    id: undefined,
-    nickname: undefined,
-    remark: undefined,
-    tagIds: []
-  }
-  formRef.value?.resetFields()
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  // 默认选中第一个
-  if (accounts.value.length > 0) {
-    queryParams.accountId = accounts.value[0].id
-  }
-  handleQuery()
-}
-
-/** 修改按钮操作 */
-const handleUpdate = (row) => {
-  reset()
-  getUser(row.id).then((data) => {
-    form.value = data
-    open.value = true
-    title.value = '修改公众号粉丝'
-  })
-}
-
-/** 提交按钮 */
-const submitForm = () => {
-  formRef.value.validate((valid) => {
-    if (!valid) {
-      return
-    }
-    // 修改的提交
-    if (form.value.id != null) {
-      updateUser(form.value).then(() => {
-        message.success('修改成功')
-        open.value = false
-        getList()
-      })
-    }
-  })
-}
-
-/** 同步标签 */
-const handleSync = async () => {
-  const accountId = queryParams.accountId
-  try {
-    await message.confirm('是否确认同步粉丝?')
-    await syncUser(accountId)
-    message.success('开始从微信公众号同步粉丝信息,同步需要一段时间,建议稍后再查询')
-  } catch {}
-}
-</script>
diff --git a/src/views/mp/user/UserForm.vue b/src/views/mp/user/UserForm.vue
new file mode 100644
index 00000000..a23d271f
--- /dev/null
+++ b/src/views/mp/user/UserForm.vue
@@ -0,0 +1,99 @@
+<template>
+  <Dialog title="修改" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="80px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="昵称" prop="nickname">
+        <el-input v-model="formData.nickname" placeholder="请输入昵称" />
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input v-model="formData.remark" placeholder="请输入备注" />
+      </el-form-item>
+      <el-form-item label="标签" prop="tagIds">
+        <el-select v-model="formData.tagIds" multiple clearable placeholder="请选择标签">
+          <el-option
+            v-for="item in tagList"
+            :key="item.tagId"
+            :label="item.name"
+            :value="item.tagId"
+          />
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import * as MpTagApi from '@/api/mp/tag'
+import * as MpUserApi from '@/api/mp/user'
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const formLoading = ref(false) // 表单的加载中
+const formData = ref({
+  id: undefined,
+  nickname: undefined,
+  remark: undefined,
+  tagIds: []
+})
+const formRules = reactive({}) // 表单的校验
+const formRef = ref() // 表单 Ref
+const tagList = ref([]) // 公众号标签列表
+
+/** 打开弹窗 */
+const open = async (id: number) => {
+  dialogVisible.value = true
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await MpUserApi.getUser(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+  // 加载标签
+  tagList.value = await MpTagApi.getSimpleTagList()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    await MpUserApi.updateUser(formData.value)
+    message.success(t('common.updateSuccess'))
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    nickname: undefined,
+    remark: undefined,
+    tagIds: []
+  }
+  formRef.value?.resetFields()
+}
+</script>
diff --git a/src/views/mp/user/index.vue b/src/views/mp/user/index.vue
new file mode 100644
index 00000000..e338f822
--- /dev/null
+++ b/src/views/mp/user/index.vue
@@ -0,0 +1,187 @@
+<template>
+  <doc-alert title="公众号粉丝" url="https://doc.iocoder.cn/mp/user/" />
+
+  <!-- 搜索工作栏 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="公众号" prop="accountId">
+        <el-select v-model="queryParams.accountId" placeholder="请选择公众号" class="!w-240px">
+          <el-option
+            v-for="item in accountList"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="用户标识" prop="openid">
+        <el-input
+          v-model="queryParams.openid"
+          placeholder="请输入用户标识"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="昵称" prop="nickname">
+        <el-input
+          v-model="queryParams.nickname"
+          placeholder="请输入昵称"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
+        <el-button type="success" plain @click="handleSync" v-hasPermi="['mp:user:sync']">
+          <Icon icon="ep:refresh" class="mr-5px" /> 同步
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="编号" align="center" prop="id" />
+      <el-table-column label="用户标识" align="center" prop="openid" width="260" />
+      <el-table-column label="昵称" align="center" prop="nickname" />
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="标签" align="center" prop="tagIds" width="200">
+        <template #default="scope">
+          <span v-for="(tagId, index) in scope.row.tagIds" :key="index">
+            <el-tag>{{ tagList.find((tag) => tag.tagId === tagId)?.name }} </el-tag>&nbsp;
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="订阅状态" align="center" prop="subscribeStatus">
+        <template #default="scope">
+          <el-tag v-if="scope.row.subscribeStatus === 0" type="success">已订阅</el-tag>
+          <el-tag v-else type="danger">未订阅</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="订阅时间"
+        align="center"
+        prop="subscribeTime"
+        width="180"
+        :formatter="dateFormatter"
+      />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button
+            type="primary"
+            link
+            @click="openForm(scope.row.id)"
+            v-hasPermi="['mp:user:update']"
+          >
+            修改
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:修改 -->
+  <UserForm ref="formRef" @success="getList" />
+</template>
+<script lang="ts" setup name="MpUser">
+import { dateFormatter } from '@/utils/formatTime'
+import * as MpAccountApi from '@/api/mp/account'
+import * as MpUserApi from '@/api/mp/user'
+import * as MpTagApi from '@/api/mp/tag'
+import UserForm from './UserForm.vue'
+const message = useMessage() // 消息
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  accountId: null,
+  openid: null,
+  nickname: null
+})
+const queryFormRef = ref() // 搜索的表单
+const accountList = ref([]) // 公众号账号列表
+const tagList = ref([]) // 公众号标签列表
+
+/** 查询列表 */
+const getList = async () => {
+  // 如果没有选中公众号账号,则进行提示。
+  if (!queryParams.accountId) {
+    message.error('未选中公众号,无法查询用户')
+    return false
+  }
+  try {
+    loading.value = true
+    const data = await MpUserApi.getUserPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  // 默认选中第一个
+  if (accountList.value.length > 0) {
+    queryParams.accountId = accountList.value[0].id
+  }
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (id: number) => {
+  formRef.value.open(id)
+}
+
+/** 同步标签 */
+const handleSync = async () => {
+  const accountId = queryParams.accountId
+  try {
+    await message.confirm('是否确认同步粉丝?')
+    await MpUserApi.syncUser(accountId)
+    message.success('开始从微信公众号同步粉丝信息,同步需要一段时间,建议稍后再查询')
+    await getList()
+  } catch {}
+}
+
+/** 初始化 */
+onMounted(async () => {
+  // 加载标签
+  tagList.value = await MpTagApi.getSimpleTagList()
+
+  // 加载账号
+  accountList.value = await MpAccountApi.getSimpleAccountList()
+  if (accountList.value.length > 0) {
+    queryParams.accountId = accountList.value[0].id
+  }
+  await getList()
+})
+</script>