上传文件至 src/views

This commit is contained in:
2025-05-09 13:29:24 +09:00
parent 4bf8637494
commit fcb54c6411
4 changed files with 720 additions and 0 deletions

238
src/views/OutStore.vue Normal file
View File

@ -0,0 +1,238 @@
<template>
<div class="page-container">
<UserMenu />
<form @submit.prevent="search" class="search-form">
<button @click="scanQR">QRコード読取</button>
<div v-show="isScanning">
<div id="reader" style="width: 300px"></div>
</div>
<input
id="target"
v-model="form.id"
type="number"
placeholder="資材IDを入力"
required
/>
<button type="submit">検索</button>
</form>
<form @submit.prevent="submitForm">
<table class="inputtable">
<tr>
<td>資材ID</td>
<td>{{ form.id }}</td>
</tr>
<tr>
<td>資材名</td>
<td>{{ form.name }}</td>
</tr>
<tr>
<td>数量</td>
<td>{{ form.num }}</td>
</tr>
<tr>
<td>カテゴリー</td>
<td>{{ form.category_name }}</td>
</tr>
<tr>
<td>状態</td>
<td>{{ form.status_name }}</td>
</tr>
<tr>
<td>倉庫</td>
<td>{{ form.souko_name || "" }}</td>
</tr>
<tr>
<td>購入依頼者</td>
<td>{{ form.request_user_name }}</td>
</tr>
<tr>
<td>購入依頼部門</td>
<td>{{ form.request_dept_name }}</td>
</tr>
<tr>
<td>依頼日</td>
<td>{{ form.request_date }}</td>
</tr>
<tr>
<td>発注日</td>
<td>{{ form.order_date }}</td>
</tr>
<tr>
<td>納品日</td>
<td>{{ form.delivery_date }}</td>
</tr>
<tr>
<td>入庫日</td>
<td>{{ form.instore_date }}</td>
</tr>
<tr>
<td>出庫日</td>
<td>{{ form.outstore_date }}</td>
</tr>
<tr>
<td>備考</td>
<td>{{ form.note }}</td>
</tr>
</table>
<div v-if="form.status === '6'" style="margin-top: 20px">
<button type="submit">出庫</button>
</div>
</form>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from "vue";
import axios from "axios";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import UserMenu from "../components/UserMenu.vue";
import { Html5Qrcode } from "html5-qrcode";
const router = useRouter();
const form = reactive({
id: "",
name: "",
num: "",
category_name: "",
status_name: "",
status: 0,
souko_id: "",
souko_name: "",
request_user_name: "",
request_dept_name: "",
request_date: "",
order_date: "",
delivery_date: "",
instore_date: "",
outstore_date: "",
note: "",
deptId: "",
});
const state = reactive({
soukoList: [],
});
const isScanning = ref(false);
const scanQR = () => {
isScanning.value = true;
const html5QrCode = new Html5Qrcode("reader");
html5QrCode
.start(
{ facingMode: "environment" },
{
fps: 10,
qrbox: 250,
},
(decodedText) => {
form.id = decodedText;
isScanning.value = false;
html5QrCode
.stop()
.then(() => {
/* 停止成功時の処理 */
})
.catch(() => {
/* 停止失敗時の処理 */
});
search();
},
(errorMessage) => {
console.warn("読み取りエラー: ", errorMessage);
}
)
.catch((err) => {
ElMessage.error("QRコード読み取りに失敗しました");
isScanning.value = false;
});
};
const fetchList = async () => {
try {
const res = await axios.get("/api/outStore/init");
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "初期化に失敗しました");
router.push("/login");
}
} catch (err) {
ElMessage.error("サーバーエラーが発生しました");
router.push("/login");
}
};
const search = async () => {
if (!form.id || form.id === "") {
ElMessage.warning("資材IDを入力してください");
return;
}
try {
const res = await axios.post("/api/outStore/search", { id: form.id });
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "検索失敗しました");
return;
}
const data = response.data;
if (!data) {
ElMessage.error("資材データが見つかりませんでした");
return;
}
Object.assign(form, data.sizai);
state.soukoList = data.soukoList || [];
const selectedSouko = state.soukoList.find((s) => s.id === form.souko_id);
form.souko_name = selectedSouko ? selectedSouko.name : "";
} catch (err) {
ElMessage.error("検索中にサーバーエラーが発生しました");
}
};
const submitForm = async () => {
if (!confirm("出庫に操作しますか?")) return;
axios
.post("/api/outStore/act", { sizai: { ...form } })
.then((res) => {
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "出庫失敗しました");
return;
}
Object.assign(form, response.data.sizai);
ElMessage.success(response.message || "出庫完了しました");
})
.catch(() => {
ElMessage.error("サーバーエラーが発生しました");
});
};
onMounted(fetchList);
</script>
<style scoped>
@import "@/assets/common.css";
.page-container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
}
.search-form {
margin-bottom: 20px;
display: flex;
gap: 10px;
align-items: center;
}
</style>

160
src/views/RequestDetail.vue Normal file
View File

@ -0,0 +1,160 @@
<template>
<div class="page-container">
<UserMenu />
<form @submit.prevent="submitForm">
<table class="inputtable">
<tr>
<td>資材ID</td>
<td>{{ form.id }}</td>
</tr>
<tr>
<td>資材名</td>
<td>
<input type="text" v-model="form.name" maxlength="200" required />
</td>
</tr>
<tr>
<td>数量</td>
<td>
<input
type="number"
v-model.number="form.num"
min="1"
step="1"
required
/>
</td>
</tr>
<tr>
<td>カテゴリー</td>
<td>
<select v-model="form.category_id" required>
<option disabled value="">選択してください</option>
<option v-for="cat in categoryList" :key="cat.id" :value="cat.id">
{{ cat.name }}
</option>
</select>
</td>
</tr>
<tr>
<td>購入依頼者</td>
<td>{{ form.request_user_name }}</td>
</tr>
<tr>
<td>購入依頼部門</td>
<td>{{ form.request_dept_name }}</td>
</tr>
<tr>
<td>状態</td>
<td>{{ form.status_name }}</td>
</tr>
<tr>
<td>依頼日</td>
<td>{{ form.request_date }}</td>
</tr>
<tr>
<td>備考</td>
<td>
<textarea
class="noteTextarea"
v-model="form.note"
maxlength="2000"
></textarea>
</td>
</tr>
</table>
<div style="margin-top: 20px">
<input type="submit" value="更新" />
</div>
</form>
<div style="margin-top: 20px">
<router-link to="/request">依頼一覧へ戻る</router-link>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import axios from "axios";
import { ElMessage } from "element-plus";
import UserMenu from "../components/UserMenu.vue";
const router = useRouter();
const route = useRoute();
const form = reactive({
id: "",
name: "",
num: 1,
category_id: "",
request_user_name: "",
request_dept_name: "",
status_name: "",
request_date: "",
note: "",
});
const categoryList = ref([]);
const fetchDetail = async () => {
try {
const res = await axios.get("/api/requestDetail/init", {
params: { targetId: route.query.targetId },
});
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "データ取得に失敗しました");
router.push("/request");
return;
}
Object.assign(form, response.data.sizai);
categoryList.value = response.data.categoryModelList || [];
} catch (err) {
ElMessage.error("サーバーエラーが発生しました");
console.error(err);
router.push("/request");
}
};
onMounted(fetchDetail);
const submitForm = async () => {
try {
const res = await axios.post("/api/requestDetail/modify", {
sizai: { ...form },
});
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "更新に失敗しました");
return;
}
ElMessage.success(response.message || "更新が完了しました");
Object.assign(form, response.data.sizai);
categoryList.value = response.data.categoryModelList || [];
} catch (err) {
ElMessage.error("サーバーエラーが発生しました");
console.error(err);
}
};
</script>
<style scoped>
@import "@/assets/common.css";
.noteTextarea {
width: 100%;
height: 150px;
resize: none;
}
.inputtable td {
padding: 5px;
}
</style>

177
src/views/RequestList.vue Normal file
View File

@ -0,0 +1,177 @@
<template>
<div class="page-container">
<UserMenu />
<form @submit.prevent="search" class="search-form">
<label>From:</label>
<input type="date" v-model="form.startDate" required />
<label>To:</label>
<input type="date" v-model="form.endDate" required />
<button type="submit">検索</button>
</form>
<table v-if="form.sizaiList?.length > 0" class="outputtable">
<thead>
<tr>
<th>資材ID</th>
<th>資材名</th>
<th>数量</th>
<th>カテゴリー</th>
<th>購入依頼者</th>
<th>購入依頼部門</th>
<th>状態</th>
<th>依頼日</th>
<th>撤回</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in form.sizaiList" :key="index">
<td>
<router-link
:to="{ path: '/requestDetail', query: { targetId: item.id } }"
>
{{ item.id }}
</router-link>
</td>
<td>{{ item.name }}</td>
<td>{{ item.num }}</td>
<td>{{ item.category_name }}</td>
<td>{{ item.request_user_name }}</td>
<td>{{ item.request_dept_name }}</td>
<td>{{ item.status_name }}</td>
<td>{{ item.request_date }}</td>
<td>
<template v-if="item.status == 1 || item.status == 3">
<a href="#" @click.prevent="deleteItem(item.id)">撤回</a>
</template>
<template v-else>
<span style="color: red">撤回不可</span>
</template>
</td>
</tr>
</tbody>
</table>
<div v-else style="text-align: center; margin-top: 20px">
データがありません
</div>
<div style="margin-top: 20px">
<router-link to="/requestRegist">購入依頼登録</router-link>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted } from "vue";
import axios from "axios";
import UserMenu from "../components/UserMenu.vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
const router = useRouter();
const form = reactive({
startDate: "",
endDate: "",
sizaiList: [],
deleteById: null,
message: "",
messageflag: true,
});
const fetchList = () => {
axios
.get("/api/requestList/init")
.then((res) => {
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "データ取得に失敗しました");
return;
}
Object.assign(form, response.data);
if (!form.sizaiList) form.sizaiList = [];
})
.catch((err) => {
if ([401, 403].includes(err.response?.status)) {
ElMessage.error("ログイン情報が無効です。再度ログインしてください。");
router.push("/login");
} else {
ElMessage.error("サーバーエラーが発生しました");
}
});
};
onMounted(() => {
const today = new Date();
const oneMonthAgo = new Date();
oneMonthAgo.setMonth(today.getMonth() - 1);
form.startDate = oneMonthAgo.toISOString().split("T")[0];
form.endDate = today.toISOString().split("T")[0];
fetchList(); // 立刻加载一次
});
const search = () => {
axios
.post("/api/requestList/search", {
startDate: form.startDate,
endDate: form.endDate,
})
.then((res) => {
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "検索に失敗しました");
return;
}
Object.assign(form, response.data);
if (!form.sizaiList) form.sizaiList = [];
})
.catch(() => {
ElMessage.error("サーバーエラーが発生しました");
});
};
const deleteItem = (id) => {
if (!confirm("本当に撤回しますか?")) return;
axios
.post("/api/requestList/delete", {
deleteById: id,
startDate: form.startDate,
endDate: form.endDate,
})
.then((res) => {
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "削除に失敗しました");
return;
}
Object.assign(form, response.data);
if (!form.sizaiList) form.sizaiList = [];
})
.catch(() => {
ElMessage.error("サーバーエラーが発生しました");
});
};
</script>
<style scoped>
@import "@/assets/common.css";
.page-container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
}
.search-form {
margin-bottom: 20px;
display: flex;
gap: 10px;
align-items: center;
}
</style>

145
src/views/RequestRegist.vue Normal file
View File

@ -0,0 +1,145 @@
<template>
<div class="page-container">
<UserMenu />
<form @submit.prevent="submitForm" @reset="resetForm">
<table class="inputtable">
<tr>
<td>資材名</td>
<td>
<input type="text" v-model="form.name" maxlength="200" required />
</td>
</tr>
<tr>
<td>数量</td>
<td>
<input
type="number"
v-model.number="form.num"
min="1"
step="1"
required
/>
</td>
</tr>
<tr>
<td>カテゴリー</td>
<td>
<select v-model="form.category_id" required>
<option disabled value="">選択してください</option>
<option v-for="cat in categoryList" :key="cat.id" :value="cat.id">
{{ cat.name }}
</option>
</select>
</td>
</tr>
<tr>
<td>備考</td>
<td>
<textarea
v-model="form.note"
maxlength="2000"
class="noteTextarea"
></textarea>
</td>
</tr>
</table>
<div style="margin-top: 20px">
<input type="submit" value="登録" />
&nbsp;
<input type="reset" value="クリア" />
</div>
</form>
<div style="margin-top: 20px">
<router-link to="/request">依頼一覧へ戻る</router-link>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from "vue";
import { useRouter } from "vue-router";
import axios from "axios";
import UserMenu from "../components/UserMenu.vue";
import { ElMessage } from "element-plus";
const router = useRouter();
const form = reactive({
name: "",
num: 1,
category_id: "",
note: "",
});
const categoryList = ref([]);
const fetchCategoryList = async () => {
try {
const res = await axios.get("/api/requestRegist/init");
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "カテゴリーの取得に失敗しました");
return;
}
categoryList.value = response.data.categoryModelList || [];
} catch (err) {
ElMessage.error("サーバーエラーが発生しました");
console.error(err);
}
};
onMounted(fetchCategoryList);
const resetForm = () => {
form.name = "";
form.num = 1;
form.category_id = "";
form.note = "";
};
const submitForm = async () => {
try {
const res = await axios.post("/api/requestRegist/regist", {
sizai: {
name: form.name,
num: form.num,
category_id: form.category_id,
note: form.note,
},
});
const response = res.data;
if (!response.success) {
ElMessage.error(response.message || "登録に失敗しました");
return;
}
ElMessage.success(response.message || "登録が完了しました");
router.push("/request");
} catch (err) {
ElMessage.error("サーバーエラーが発生しました");
console.error(err);
}
};
</script>
<style scoped>
@import "@/assets/common.css";
.noteTextarea {
width: 100%;
height: 150px;
resize: none;
}
.inputtable td {
padding: 5px;
}
.page-container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
}
</style>